got add, remove, and edit actions working
This commit is contained in:
Родитель
260b6177fe
Коммит
0e0ea2ef06
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
24
package.json
24
package.json
|
@ -4,19 +4,27 @@
|
|||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "webpack-dev-server --mode development --progress",
|
||||
"start:client": "webpack-dev-server --mode development --progress",
|
||||
"build": "webpack --mode production",
|
||||
"test": "jest --watch"
|
||||
"test": "jest --watch",
|
||||
"start:server": "nodemon server/index.js",
|
||||
"start": "run-p start:server start:client"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.16.1",
|
||||
"@types/body-parser": "^1.17.0",
|
||||
"@types/node": "~10.12.21",
|
||||
"@types/jest": "^23.3.13",
|
||||
"@types/react": "^16.7.20",
|
||||
"@types/react-dom": "^16.0.11",
|
||||
"@types/react-redux": "^7.0.0",
|
||||
"@types/redux": "^3.6.0",
|
||||
"@types/cors": "^2.8.4",
|
||||
"body-parser": "^1.18.3",
|
||||
"cors": "^2.8.5",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"jest": "^23.6.0",
|
||||
"ts-jest": "^23.10.5",
|
||||
|
@ -24,15 +32,19 @@
|
|||
"typescript": "^3.2.4",
|
||||
"webpack": "^4.28.4",
|
||||
"webpack-cli": "^3.2.1",
|
||||
"webpack-dev-server": "^3.1.14"
|
||||
"webpack-dev-server": "^3.1.14",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"nodemon": "^1.18.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"office-ui-fabric-react": "^6.128.0",
|
||||
"@uifabric/experiments": "^6.51.1",
|
||||
"express": "^4.16.4",
|
||||
"immer": "^1.12.1",
|
||||
"office-ui-fabric-react": "^6.128.0",
|
||||
"react": "^16.7.0",
|
||||
"react-dom": "^16.7.0",
|
||||
"redux": "^4.0.1",
|
||||
"react-redux": "^6.0.0",
|
||||
"immer": "^1.12.1"
|
||||
"redux-thunk": "^2.3.0",
|
||||
"redux": "^4.0.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import { action, GenericActionTypes, GenericAction, GenericActionLookup } from '../redux-utils/action';
|
||||
import { Dispatch } from 'redux';
|
||||
import { Store } from '../store';
|
||||
import * as todosService from '../service/todosService';
|
||||
|
||||
let counter = 0;
|
||||
|
||||
export const actions = {
|
||||
add: (label: string) => action('add', { label }),
|
||||
add: (label: string) => action('add', { id: String(counter++), label }),
|
||||
remove: (id: string) => action('remove', { id }),
|
||||
edit: (id: string, label: string) => action('edit', { id, label }),
|
||||
complete: (id: string) => action('complete', { id }),
|
||||
|
@ -9,6 +14,46 @@ export const actions = {
|
|||
filter: (filterTypes: string) => action('filter', { filter: filterTypes })
|
||||
};
|
||||
|
||||
export const actionsWithService = {
|
||||
add: (label: string) => {
|
||||
return async (dispatch: Dispatch<TodoAction>, getState: () => Store) => {
|
||||
const addAction = actions.add(label);
|
||||
const id = addAction.id;
|
||||
dispatch(addAction);
|
||||
await todosService.add(id, getState().todos[id]);
|
||||
};
|
||||
},
|
||||
|
||||
edit: (id: string, label: string) => {
|
||||
return async (dispatch: Dispatch<TodoAction>, getState: () => Store) => {
|
||||
dispatch(actions.edit(id, label));
|
||||
await todosService.edit(id, getState().todos[id]);
|
||||
};
|
||||
},
|
||||
|
||||
remove: (id: string) => {
|
||||
return async (dispatch: Dispatch<TodoAction>, getState: () => Store) => {
|
||||
dispatch(actions.remove(id));
|
||||
await todosService.remove(id);
|
||||
};
|
||||
},
|
||||
|
||||
complete: (id: string) => {
|
||||
return async (dispatch: Dispatch<TodoAction>, getState: () => Store) => {
|
||||
dispatch(actions.complete(id));
|
||||
await todosService.edit(id, getState().todos[id]);
|
||||
};
|
||||
},
|
||||
|
||||
clear: () => {
|
||||
return async (dispatch: Dispatch<TodoAction>, getState: () => Store) => {
|
||||
dispatch(actions.clear());
|
||||
await todosService.editBulk(getState().todos);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export type ActionTypes = GenericActionTypes<typeof actions>;
|
||||
export type TodoAction = GenericAction<typeof actions>;
|
||||
export type TodoActionWithService = GenericAction<typeof actionsWithService>;
|
||||
export type TodoActionLookup = GenericActionLookup<typeof actions>;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { actions, TodoAction } from '../actions';
|
||||
import { actions, actionsWithService } from '../actions';
|
||||
import { Store, FilterTypes } from '../store';
|
||||
import { connect } from 'react-redux';
|
||||
import { Dispatch } from 'redux';
|
||||
|
@ -11,12 +11,12 @@ export function mapStateToProps({ todos, filter }: Store) {
|
|||
};
|
||||
}
|
||||
|
||||
export function mapDispatchToProps(dispatch: Dispatch<TodoAction>) {
|
||||
export function mapDispatchToProps(dispatch: any) {
|
||||
return {
|
||||
add: (label: string) => dispatch(actions.add(label)),
|
||||
remove: (id: string) => dispatch(actions.remove(id)),
|
||||
complete: (id: string) => dispatch(actions.complete(id)),
|
||||
edit: (id: string, label: string) => dispatch(actions.edit(id, label)),
|
||||
add: (label: string) => dispatch(actionsWithService.add(label)),
|
||||
remove: (id: string) => dispatch(actionsWithService.remove(id)),
|
||||
complete: (id: string) => dispatch(actionsWithService.complete(id)),
|
||||
edit: (id: string, label: string) => dispatch(actionsWithService.edit(id, label)),
|
||||
clear: () => dispatch(actions.clear()),
|
||||
setFilter: (filter: FilterTypes) => dispatch(actions.filter(filter))
|
||||
};
|
||||
|
|
|
@ -1,18 +1,36 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { createStore } from 'redux';
|
||||
import { createStore, applyMiddleware, compose } from 'redux';
|
||||
import { Provider } from 'react-redux';
|
||||
import { reducer } from './reducers';
|
||||
import { TodoAppContainer } from './components/TodoAppContainer';
|
||||
import { initializeIcons } from '@uifabric/icons';
|
||||
import thunk from 'redux-thunk';
|
||||
import * as todosService from './service/todosService';
|
||||
import { FilterTypes } from './store';
|
||||
|
||||
declare var window: any;
|
||||
|
||||
const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
|
||||
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
||||
|
||||
// For preloading store
|
||||
initializeIcons();
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<TodoAppContainer />
|
||||
</Provider>,
|
||||
document.getElementById('app')
|
||||
);
|
||||
|
||||
(async () => {
|
||||
const preloadStore = {
|
||||
todos: await todosService.getAll(),
|
||||
filter: 'all' as FilterTypes
|
||||
};
|
||||
|
||||
const store = createStore(reducer, preloadStore, composeEnhancers(applyMiddleware(thunk)));
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<TodoAppContainer />
|
||||
</Provider>,
|
||||
document.getElementById('app')
|
||||
);
|
||||
})();
|
||||
|
||||
// For Synchronous Case
|
||||
// const store = createStore(reducer, { todos: {}, filter: 'all' }, composeEnhancers(applyMiddleware(thunk)));
|
||||
|
|
|
@ -2,15 +2,12 @@ import { createReducer } from './createReducer';
|
|||
import { Store, FilterTypes } from '../store';
|
||||
import { combineReducers } from 'redux';
|
||||
|
||||
let counter = 0;
|
||||
|
||||
export const reducer = combineReducers<Store>({
|
||||
todos: createReducer<Store['todos']>(
|
||||
{},
|
||||
{
|
||||
add(draft, action) {
|
||||
const id = String(counter++);
|
||||
draft[id] = { label: action.label, completed: false };
|
||||
draft[action.id] = { label: action.label, completed: false };
|
||||
return draft;
|
||||
},
|
||||
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
import { TodoItem, Store } from '../store';
|
||||
const HOST = 'http://localhost:3000';
|
||||
|
||||
export async function add(id: string, todo: TodoItem) {
|
||||
const response = await fetch(`${HOST}/todos/${id}`, {
|
||||
method: 'post',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify(todo)
|
||||
});
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
export async function edit(id: string, todo: TodoItem) {
|
||||
const response = await fetch(`${HOST}/todos/${id}`, {
|
||||
method: 'put',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify(todo)
|
||||
});
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
export async function remove(id: string) {
|
||||
const response = await fetch(`${HOST}/todos/${id}`, {
|
||||
method: 'delete'
|
||||
});
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
export async function getAll() {
|
||||
const response = await fetch(`${HOST}/todos`, {
|
||||
method: 'get'
|
||||
});
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
export async function editBulk(todos: Store['todos']) {
|
||||
const response = await fetch(`${HOST}/todos`, {
|
||||
method: 'post',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify(todos)
|
||||
});
|
||||
|
||||
return await response.json();
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// @ts-check
|
||||
const express = require('express');
|
||||
const bodyParser = require('body-parser');
|
||||
const cors = require('cors');
|
||||
const app = express();
|
||||
|
||||
const store = {
|
||||
/** @type {any} */
|
||||
todos: {}
|
||||
};
|
||||
|
||||
app.use(bodyParser.json());
|
||||
app.use(cors());
|
||||
|
||||
app.get('/todos', (req, res) => {
|
||||
res.json(store.todos);
|
||||
});
|
||||
|
||||
app.put('/todos/:id', (req, res) => {
|
||||
store.todos[req.params.id] = req.body;
|
||||
res.json('ok');
|
||||
});
|
||||
|
||||
app.post('/todos/:id', (req, res) => {
|
||||
store.todos[req.params.id] = req.body;
|
||||
});
|
||||
|
||||
app.delete('/todos/:id', (req, res) => {
|
||||
delete store.todos[req.body.id];
|
||||
});
|
||||
|
||||
app.post('/todos', (req, res) => {
|
||||
store.todos = req.body;
|
||||
});
|
||||
|
||||
app.listen(3000, () => {
|
||||
console.log('Listening at http://localhost:3000');
|
||||
});
|
|
@ -7,6 +7,7 @@
|
|||
"esModuleInterop": true,
|
||||
"target": "es5",
|
||||
"jsx": "react",
|
||||
"allowJs": true
|
||||
"allowJs": true,
|
||||
"lib": ["es2015", "dom"]
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче