Table of Contents
1. store.ts
2. reducer.ts
3. ReduxProvider.tsx
4. mapStateToProps.ts
5. mapDispatchToProps.ts
6. Todo component (outermost package ReduxProvider
7. Subcomponents involved in the Todo component
1) TodoInput.tsx
2) TodoList.tsx
3) TodoItem.tsx
8. App components use Todo components
1. store.ts
Reference website: GitHub – zalmoxisus/redux-devtools-extension: Redux DevTools extension.
Download the Google extension plug-in address: react-redux-devtools: react-devtools-v3react-devtools-v4redux-devtools
import { applyMiddleware, combineReducers, legacy_createStore as createStore } from 'redux'; import { composeWithDevTools } from "redux-devtools-extension"; import thunk from 'redux-thunk'; import { todos, visibilityFilter } from './reduer'; // Use redux-devtools const composeEnhancers = composeWithDevTools({ // options like actionSanitizer, stateSanitizer }); //Use redux-thunk middleware const enhancer = composeEnhancers(applyMiddleware(thunk)); export default createStore(combineReducers({ todos, visibilityFilter }), enhancer);
2. reducer.ts
export const visibilityFilter = (state = 'SHOW_ALL', action) => { switch (action.type) { //Set display type (all, completed, unfinished) case 'SET_VISIBILITY_FILTER': return action.filter //The default is SHOW_ALL default: return state } } //Add list data and change array data // Split the business logic into a separate file to facilitate status management export interface StateProps { id: number; text: string; isFinished: boolean; } export interface ActionProps { type: string; [key: string]: any; } export const todos = (state: StateProps[] | [], action: ActionProps) => { console.log(state, action); switch (action.type) { case "ADD": return [...state, action.todo]; case "CHANGESTATUS": return state.map((item) => { if (item.id === action.id) { return Object.assign({}, item, { isFinished: !item.isFinished }); } return item; }); default: return state || []; } };
3. ReduxProvider.tsx
import React from "react"; import { Provider } from "react-redux"; import store from './store'; const ReduxProvider = (props) => { return <Provider store={store}>{props.children}</Provider>; }; export default ReduxProvider
4. mapStateToProps.ts
// Different types of todo lists const getVisibleTodos = (todos, filter) => { switch (filter) { case "SHOW_ALL": // Show all return todos; case "SHOW_FINISHED": return todos.filter((t) => t.isFinished); case "SHOW_NOT_FINISH": return todos.filter((t) => !t.isFinished); default: return todos; } }; const mapStateToProps = (state) => { return { // Filter data based on completion status todoList: getVisibleTodos(state.todos, state.visibilityFilter), }; }; export default mapStateToProps;
5. mapDispatchToProps.ts
import { StateProps } from "./reduer"; const mapDispatchToProps = (dispatch) => { const changeTodo = (id: number) => { dispatch({ type: "CHANGESTATUS", id }); }; // add todo const addTodo = (todo: StateProps) => { dispatch({ type: "ADD", todo }); }; // show completed const showFinished = () => { dispatch({ type: "SET_VISIBILITY_FILTER", filter: 'SHOW_FINISHED' }); } //Show unfinished const showNotFinish = () => { dispatch({ type: "SET_VISIBILITY_FILTER", filter: 'SHOW_NOT_FINISH' }); } // Show all completed const showAll = () => { dispatch({ type: "SET_VISIBILITY_FILTER", filter: 'SHOW_ALL' }); } return { addTodo, //Switch completion status changeTodo, showFinished, showNotFinish, showAll, }; }; export default mapDispatchToProps
6. Todo component (outermost package ReduxProvider
import React from "react"; import ReduxProvider from "./ReduxProvider"; import { TodoInput } from "./TodoInput"; import { TodoList } from "./TodoList"; // parent component export const Todo = () => { return ( <ReduxProvider> <TodoInput /> <TodoList /> </ReduxProvider> ) }
7. Subcomponents involved in Todo component
1) TodoInput.tsx
import React from "react"; import { useState } from "react"; import mapStateToProps from "./mapStateToProps"; import mapDispatchToProps from "./mapDispatchToProps"; import { connect } from "react-redux"; // Subassembly const TodoInput0 = (props) => { const [text, setText] = useState(""); const { addTodo, showAll, showFinished, showNotFinish } = props; const handleChangeText = (e: React.ChangeEvent) => { setText((e.target as HTMLInputElement).value); }; const handleAddTodo = () => { if (!text) return; addTodo({ id: new Date().getTime(), text: text, isFinished: false, }); setText(""); }; return ( <div className="todo-input"> <input type="text" placeholder="Please enter to-do items" onChange={handleChangeText} value={text} /> <button style={<!-- -->{ marginLeft: "10px" }} onClick={handleAddTodo}> + add </button> <button style={<!-- -->{ marginLeft: "10px" }} onClick={showAll}> show all </button> <button style={<!-- -->{ marginLeft: "10px" }} onClick={showFinished}> show finished </button> <button style={<!-- -->{ marginLeft: "10px" }} onClick={showNotFinish}> show not finish </button> </div> ); }; const TodoInput = connect(mapStateToProps, mapDispatchToProps)(TodoInput0); export { TodoInput };
2) TodoList.tsx
import React from "react"; import { TodoItem } from "./TodoItem"; import _ from "lodash"; import { connect } from "react-redux"; import mapStateToProps from "./mapStateToProps"; import mapDispatchToProps from "./mapDispatchToProps"; const TodoList0 = (props) => { const { todoList } = props; return ( <div className="todo-list"> {_.map(todoList, (item) => ( <TodoItem key={_.get(item, "id")} todo={item || {}} /> ))} </div> ); }; const TodoList = connect(mapStateToProps, mapDispatchToProps)(TodoList0); export { TodoList };
3) TodoItem.tsx
import React from "react"; import _ from "lodash"; import { connect } from "react-redux"; import mapStateToProps from "./mapStateToProps"; import mapDispatchToProps from "./mapDispatchToProps"; // grandson component const TodoItem0 = (props) => { const { todo, changeTodo } = props; //Change event status const handleChange = () => { changeTodo(_.get(todo, "id")); }; return ( <div className="todo-item"> <input type="checkbox" checked={todo.isFinished} onChange={handleChange} /> <span style={<!-- -->{ textDecoration: todo.isFinished ? "line-through" : "none" }} > {todo.text} </span> </div> ); }; const TodoItem = connect(mapStateToProps, mapDispatchToProps)(TodoItem0); export { TodoItem };
8. App component uses Todo component
import React from "react"; import { Todo } from "./reduxConnectProvider/Todo"; const App: React.FC = () => { return ( <> <Todo /> </> ); }; export default App;
Effect diagram