diff --git a/playground/src/actions/index.ts b/playground/src/actions/index.ts index 603bc0f..b99f899 100644 --- a/playground/src/actions/index.ts +++ b/playground/src/actions/index.ts @@ -10,7 +10,7 @@ export const actions = { edit: (id: string, label: string) => action('edit', { id, label }), complete: (id: string) => action('complete', { id }), clear: () => action('clear'), - filter: (filterTypes: string) => action('filter', { filter: filterTypes }) + setFilter: (filter: string) => action('setFilter', { filter }) }; export const actionsWithService = { diff --git a/playground/src/components/TodoApp.tsx b/playground/src/components/TodoApp.tsx index 6cb2e06..82a6530 100644 --- a/playground/src/components/TodoApp.tsx +++ b/playground/src/components/TodoApp.tsx @@ -3,31 +3,13 @@ import { Stack } from '@uifabric/experiments'; import { TodoFooter } from './TodoFooter'; import { TodoHeader } from './TodoHeader'; import { TodoList } from './TodoList'; -import { TodoItem, FilterTypes } from '../store'; -export interface TodoAppProps { - todos: { [id: string]: TodoItem }; - filter: FilterTypes; - add: (label: string) => void; - remove: (id: string) => void; - edit: (id: string, label: string) => void; - complete: (id: string) => void; - clear: () => void; - setFilter: (filter: FilterTypes) => void; -} - -export class TodoApp extends React.Component { - render() { - const { todos, filter, add, remove, setFilter, complete, clear, edit } = this.props; - - return ( - - - - - - - - ); - } -} +export const TodoApp = (props: {}) => ( + + + + + + + +); diff --git a/playground/src/components/TodoAppContainer.tsx b/playground/src/components/TodoAppContainer.tsx deleted file mode 100644 index 4cc2935..0000000 --- a/playground/src/components/TodoAppContainer.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { actions, actionsWithService } from '../actions'; -import { Store, FilterTypes } from '../store'; -import { connect } from 'react-redux'; -import { Dispatch } from 'redux'; -import { TodoApp } from './TodoApp'; - -export function mapStateToProps({ todos, filter }: Store) { - return { - todos, - filter - }; -} - -export function mapDispatchToProps(dispatch: any) { - return { - 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(actionsWithService.clear()), - setFilter: (filter: FilterTypes) => dispatch(actions.filter(filter)) - }; -} - -export const TodoAppContainer = connect( - mapStateToProps, - mapDispatchToProps -)(TodoApp); diff --git a/playground/src/components/TodoFooter.tsx b/playground/src/components/TodoFooter.tsx index 1c4b066..eca19d4 100644 --- a/playground/src/components/TodoFooter.tsx +++ b/playground/src/components/TodoFooter.tsx @@ -1,14 +1,30 @@ import React from 'react'; import { Text, Stack } from '@uifabric/experiments'; -import { TodoItem } from '../store'; +import { TodoItem, Store } from '../store'; import { DefaultButton } from 'office-ui-fabric-react'; +import { actionsWithService } from '../actions'; +import { connect } from 'react-redux'; -export interface TodoFooterProps { - todos: { [id: string]: TodoItem }; - clear: () => void; +// Redux Container +export function mapStateToProps({ todos, filter }: Store) { + return { + todos, + filter + }; } -export const TodoFooter = (props: TodoFooterProps) => { +export function mapDispatchToProps(dispatch: any) { + return { + clear: () => dispatch(actionsWithService.clear()) + }; +} + +type TodoFooterProps = ReturnType & ReturnType; + +export const TodoFooter = connect( + mapStateToProps, + mapDispatchToProps +)((props: TodoFooterProps) => { const itemCount = Object.keys(props.todos).filter(id => !props.todos[id].completed).length; return ( @@ -19,4 +35,4 @@ export const TodoFooter = (props: TodoFooterProps) => { props.clear()}>Clear Completed ); -}; +}); diff --git a/playground/src/components/TodoHeader.tsx b/playground/src/components/TodoHeader.tsx index 5468871..71e4d15 100644 --- a/playground/src/components/TodoHeader.tsx +++ b/playground/src/components/TodoHeader.tsx @@ -1,19 +1,32 @@ import React from 'react'; import { Text, Stack } from '@uifabric/experiments'; import { Pivot, PivotItem, TextField } from 'office-ui-fabric-react'; -import { FilterTypes } from '../store'; +import { FilterTypes, Store } from '../store'; +import { actionsWithService, actions } from '../actions'; +import { connect } from 'react-redux'; -export interface TodoHeaderProps { - add: (label: string) => void; - remove: (id: string) => void; - filter: (filter: FilterTypes) => void; +function mapStateToProps({ todos, filter }: Store) { + return { + todos, + filter + }; } -export interface TodoHeaderState { +function mapDispatchToProps(dispatch: any) { + return { + add: (label: string) => dispatch(actionsWithService.add(label)), + remove: (id: string) => dispatch(actionsWithService.remove(id)), + setFilter: (filter: FilterTypes) => dispatch(actions.setFilter(filter)) + }; +} + +type TodoHeaderProps = ReturnType & ReturnType; + +interface TodoHeaderState { labelInput: string; } -export class TodoHeader extends React.Component { +class TodoHeader extends React.Component { constructor(props: TodoHeaderProps) { super(props); this.state = { labelInput: undefined }; @@ -31,7 +44,7 @@ export class TodoHeader extends React.Component { - this.props.filter(item.props.headerText as FilterTypes); + this.props.setFilter(item.props.headerText as FilterTypes); }; render() { @@ -57,3 +70,11 @@ export class TodoHeader extends React.Component void; - complete: (id: string) => void; - remove: (id: string) => void; +function mapStateToProps({ todos, filter }: Store) { + return { + todos, + filter + }; } -export class TodoList extends React.Component { +function mapDispatchToProps(dispatch: any) {} + +type TodoListProps = ReturnType & ReturnType; + +class TodoList extends React.Component { render() { const { filter, todos } = this.props; let filteredTodos: typeof todos = {}; @@ -42,19 +46,16 @@ export class TodoList extends React.Component { {Object.keys(filteredTodos).map(id => { const todo = filteredTodos[id]; - return ( - - ); + return ; })} ); } } + +const component = connect( + mapStateToProps, + mapDispatchToProps +)(TodoList); + +export { component as TodoList }; diff --git a/playground/src/components/TodoListItem.tsx b/playground/src/components/TodoListItem.tsx index b4d9932..4086b3c 100644 --- a/playground/src/components/TodoListItem.tsx +++ b/playground/src/components/TodoListItem.tsx @@ -2,17 +2,27 @@ import React from 'react'; import { Stack, Text } from '@uifabric/experiments'; import { Checkbox, IconButton, TextField } from 'office-ui-fabric-react'; import { mergeStyles } from '@uifabric/styling'; +import { Store, FilterTypes } from '../store'; +import { actionsWithService, actions } from '../actions'; +import { connect } from 'react-redux'; -export interface TodoListItemProps { - id: string; - checked: boolean; - label: string; - edit: (id: string, label: string) => void; - complete: (id: string) => void; - remove: (id: string) => void; +function mapStateToProps({ todos }: Store) { + return { + todos + }; } -export interface TodoListItemState { +function mapDispatchToProps(dispatch: any) { + return { + remove: (id: string) => dispatch(actionsWithService.remove(id)), + complete: (id: string) => dispatch(actionsWithService.complete(id)), + edit: (id: string, label: string) => dispatch(actionsWithService.edit(id, label)) + }; +} + +type TodoListItemProps = { id: string } & ReturnType & ReturnType; + +interface TodoListItemState { editing: boolean; editLabel: string; } @@ -28,7 +38,7 @@ const className = mergeStyles({ } }); -export class TodoListItem extends React.Component { +class TodoListItem extends React.Component { /** * */ @@ -38,9 +48,12 @@ export class TodoListItem extends React.Component { + const { todos, id } = this.props; + const { label } = todos[id]; + this.setState(prevState => ({ editing: true, - editLabel: prevState.editLabel || this.props.label + editLabel: prevState.editLabel || label })); }; @@ -63,13 +76,14 @@ export class TodoListItem extends React.Component {!this.state.editing && ( <> - complete(id)} /> + complete(id)} />
remove(id)} /> @@ -82,3 +96,10 @@ export class TodoListItem extends React.Component - + , document.getElementById('app') ); diff --git a/playground/src/reducers/index.ts b/playground/src/reducers/index.ts index 96484af..df6ccf7 100644 --- a/playground/src/reducers/index.ts +++ b/playground/src/reducers/index.ts @@ -36,7 +36,7 @@ export const reducer = combineReducers({ } } ), - filter: createReducer('all', (draft, action) => { + filter: createReducer('all', (draft, action) => { return action.filter as FilterTypes; }) }); diff --git a/server/index.js b/server/index.js index 1f0e128..dae2115 100644 --- a/server/index.js +++ b/server/index.js @@ -26,7 +26,7 @@ app.post('/todos/:id', (req, res) => { }); app.delete('/todos/:id', (req, res) => { - delete store.todos[req.body.id]; + delete store.todos[req.params.id]; }); app.post('/todos', req => { diff --git a/step06/src/App.tsx b/step06/src/App.tsx index d14f9b5..3f19668 100644 --- a/step06/src/App.tsx +++ b/step06/src/App.tsx @@ -3,28 +3,27 @@ import { TodoFooter } from './components/TodoFooter'; import { TodoHeader } from './components/TodoHeader'; import { TodoList } from './components/TodoList'; - export class TodoApp extends React.Component { constructor(props) { super(props); this.state = { todos: [ - {key: 1, text: 'Todo 1', completed: true}, - {key: 2, text: 'Todo 2'}, - {key: 3, text: 'Todo 3'}, - {key: 4, text: 'Todo 4'}, + { key: 1, text: 'Todo 1', completed: true }, + { key: 2, text: 'Todo 2' }, + { key: 3, text: 'Todo 3' }, + { key: 4, text: 'Todo 4' } ], - filter: 'all' - } + filter: 'all' + }; } render() { - const {filter, todos} = this.state; + const { filter, todos } = this.state; return ( -
- - - -
+
+ + + +
); } } diff --git a/webpack.config.js b/webpack.config.js index 4de3ab1..6918418 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -55,6 +55,6 @@ module.exports = Object.keys(entries).map(entryPoint => { }, stats: 'minimal', mode: 'development', - devtool: 'cheap-module-source-map' + devtool: 'eval' }; });