integrating immer

This commit is contained in:
Ken
2019-01-30 14:33:48 -08:00
parent a61d89ea14
commit c44af2a38f
7 changed files with 82 additions and 31 deletions

5
package-lock.json generated
View File

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

View File

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

View File

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

View File

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

View File

@@ -1,23 +1,54 @@
import React from 'react'; import React from 'react';
import { Text, Stack } from '@uifabric/experiments'; 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) => { export interface TodoHeaderState {
return ( labelInput: string;
<Stack> }
<Stack horizontal horizontalAlign="center">
<Text variant="xxLarge">todos</Text> 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> </Stack>
);
<TextField placeholder="What needs to be done?" /> }
}
<Pivot>
<PivotItem headerText="all" />
<PivotItem headerText="active" />
<PivotItem headerText="completed" />
</Pivot>
</Stack>
);
};

View File

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

View File

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