This commit is contained in:
Ken 2019-01-30 14:33:48 -08:00
Родитель a61d89ea14
Коммит c44af2a38f
7 изменённых файлов: 82 добавлений и 31 удалений

5
package-lock.json сгенерированный
Просмотреть файл

@ -3263,6 +3263,11 @@
"integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
"dev": true
},
"immer": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/immer/-/immer-1.12.1.tgz",
"integrity": "sha512-3fmKM6ovaqDt0CdC9daXpNi5x/YCYS3i4cwLdTVkhJdk5jrDXoPs7lCm3IqM3yhfSnz4tjjxbRG2CziQ7m8ztg=="
},
"import-local": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",

Просмотреть файл

@ -4,7 +4,7 @@
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack-dev-server --mode development",
"start": "webpack-dev-server --mode development --progress",
"build": "webpack --mode production"
},
"keywords": [],
@ -28,6 +28,7 @@
"react": "^16.7.0",
"react-dom": "^16.7.0",
"redux": "^4.0.1",
"react-redux": "^6.0.0"
"react-redux": "^6.0.0",
"immer": "^1.12.1"
}
}

Просмотреть файл

@ -8,18 +8,21 @@ import { TodoItem, FilterTypes } from '../store';
export interface TodoAppProps {
todos: { [id: string]: TodoItem };
filter: FilterTypes;
add: (label: string) => void;
remove: (id: string) => void;
setFilter: (filter: FilterTypes) => void;
}
export class TodoApp extends React.Component<TodoAppProps> {
render() {
const { todos, filter } = this.props;
const { todos, filter, add, remove, setFilter } = this.props;
return (
<Stack horizontalAlign="center">
<Stack style={{ width: 650 }} verticalGap={25}>
<TodoHeader />
<TodoHeader {...{ add, remove, filter }} />
<TodoList {...{ todos, filter }} />
<TodoFooter />
<TodoFooter {...{ todos, setFilter }} />
</Stack>
</Stack>
);

Просмотреть файл

@ -1,5 +1,5 @@
import * as actions from '../actions';
import { Store } from '../store';
import { Store, FilterTypes } from '../store';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import { TodoApp } from './TodoApp';
@ -14,7 +14,11 @@ export function mapStateToProps({ todos, filter }: Store) {
export function mapDispatchToProps(dispatch: Dispatch<actions.TodoAction>) {
return {
add: (label: string) => dispatch(actions.add(label)),
remove: (id: string) => dispatch(actions.remove(id))
remove: (id: string) => dispatch(actions.remove(id)),
complete: (id: string) => dispatch(actions.complete(id)),
completeAll: () => dispatch(actions.completeAll()),
clear: () => dispatch(actions.clear()),
setFilter: (filter: FilterTypes) => dispatch(actions.filter(filter))
};
}

Просмотреть файл

@ -1,23 +1,54 @@
import React from 'react';
import { Text, Stack } from '@uifabric/experiments';
import { Checkbox, Button, Pivot, PivotItem, TextField } from 'office-ui-fabric-react';
import { Pivot, PivotItem, TextField } from 'office-ui-fabric-react';
import { add } from '../actions';
export interface TodoFooterProps {}
export interface TodoHeaderProps {
add: (label: string) => void;
remove: (id: string) => void;
}
export const TodoHeader = (props: TodoFooterProps) => {
return (
<Stack>
<Stack horizontal horizontalAlign="center">
<Text variant="xxLarge">todos</Text>
export interface TodoHeaderState {
labelInput: string;
}
export class TodoHeader extends React.Component<TodoHeaderProps, TodoHeaderState> {
constructor(props: TodoHeaderProps) {
super(props);
this.state = { labelInput: undefined };
}
onKeyPress = (evt: React.KeyboardEvent) => {
if (evt.charCode === 13) {
this.props.add(this.state.labelInput);
this.setState({ labelInput: undefined });
}
};
onChange = (evt: React.FormEvent<HTMLInputElement>, newValue: string) => {
this.setState({ labelInput: newValue });
};
render() {
return (
<Stack>
<Stack horizontal horizontalAlign="center">
<Text variant="xxLarge">todos</Text>
</Stack>
<TextField
placeholder="What needs to be done?"
value={this.state.labelInput}
onChange={this.onChange}
onKeyPress={this.onKeyPress}
/>
<Pivot>
<PivotItem headerText="all" />
<PivotItem headerText="active" />
<PivotItem headerText="completed" />
</Pivot>
</Stack>
<TextField placeholder="What needs to be done?" />
<Pivot>
<PivotItem headerText="all" />
<PivotItem headerText="active" />
<PivotItem headerText="completed" />
</Pivot>
</Stack>
);
};
);
}
}

Просмотреть файл

@ -1,13 +1,14 @@
import { Reducer } from 'redux';
import { ActionTypes, TodoAction } from '../actions';
import { Draft, produce } from 'immer';
export function createReducer<T>(
initialState: T,
handlers: { [actionType in ActionTypes]?: (state: T, action: TodoAction) => T }
handlers: { [actionType in ActionTypes]?: (state: Draft<T>, action: TodoAction) => T }
): Reducer<T> {
return function reducer(state = initialState, action: TodoAction): T {
if (handlers.hasOwnProperty(action.type)) {
return handlers[action.type](state, action);
return produce(state, draft => handlers[action.type](draft, action));
} else {
return state;
}

Просмотреть файл

@ -5,17 +5,23 @@ import { combineReducers } from 'redux';
let counter = 0;
export const reducer = combineReducers<Store>({
todos: createReducer(
todos: createReducer<Store['todos']>(
{},
{
add(state, action) {
add(draft, action) {
const id = String(counter++);
return { ...state, [id]: { label: action.label, completed: false } };
draft[id] = { label: action.label, completed: false };
return draft;
},
remove(draft, action) {
delete draft[action.id];
return draft;
}
}
),
filter: createReducer<FilterTypes>('all', {
filter(state, action) {
filter(draft, action) {
return action.filter;
}
})