diff --git a/package-lock.json b/package-lock.json index c3a36d7..609cf88 100644 --- a/package-lock.json +++ b/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", diff --git a/package.json b/package.json index f77754d..79d05ea 100644 --- a/package.json +++ b/package.json @@ -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" } } diff --git a/playground/src/components/TodoApp.tsx b/playground/src/components/TodoApp.tsx index b66911c..db15ffc 100644 --- a/playground/src/components/TodoApp.tsx +++ b/playground/src/components/TodoApp.tsx @@ -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 { render() { - const { todos, filter } = this.props; + const { todos, filter, add, remove, setFilter } = this.props; return ( - + - + ); diff --git a/playground/src/components/TodoAppContainer.tsx b/playground/src/components/TodoAppContainer.tsx index 1b366d0..fb07157 100644 --- a/playground/src/components/TodoAppContainer.tsx +++ b/playground/src/components/TodoAppContainer.tsx @@ -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) { 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)) }; } diff --git a/playground/src/components/TodoHeader.tsx b/playground/src/components/TodoHeader.tsx index 1aec605..550bb22 100644 --- a/playground/src/components/TodoHeader.tsx +++ b/playground/src/components/TodoHeader.tsx @@ -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 ( - - - todos +export interface TodoHeaderState { + labelInput: string; +} + +export class TodoHeader extends React.Component { + 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, newValue: string) => { + this.setState({ labelInput: newValue }); + }; + + render() { + return ( + + + todos + + + + + + + + + - - - - - - - - - - ); -}; + ); + } +} diff --git a/playground/src/reducers/createReducer.ts b/playground/src/reducers/createReducer.ts index 24ea0e0..0131893 100644 --- a/playground/src/reducers/createReducer.ts +++ b/playground/src/reducers/createReducer.ts @@ -1,13 +1,14 @@ import { Reducer } from 'redux'; import { ActionTypes, TodoAction } from '../actions'; +import { Draft, produce } from 'immer'; export function createReducer( initialState: T, - handlers: { [actionType in ActionTypes]?: (state: T, action: TodoAction) => T } + handlers: { [actionType in ActionTypes]?: (state: Draft, action: TodoAction) => T } ): Reducer { 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; } diff --git a/playground/src/reducers/index.ts b/playground/src/reducers/index.ts index 3d7435e..e315da2 100644 --- a/playground/src/reducers/index.ts +++ b/playground/src/reducers/index.ts @@ -5,17 +5,23 @@ import { combineReducers } from 'redux'; let counter = 0; export const reducer = combineReducers({ - todos: createReducer( + todos: createReducer( {}, { - 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('all', { - filter(state, action) { + filter(draft, action) { return action.filter; } })