mirror of
https://github.com/microsoft/frontend-bootcamp.git
synced 2026-01-26 14:56:42 +08:00
integrating immer
This commit is contained in:
5
package-lock.json
generated
5
package-lock.json
generated
@@ -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",
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user