目录
一 核心代码
1.reducer
2.store.js
二 关于context API的使用
1. MyContext
2. createContext
3. ContextProvider
4. connect
三 组件验证效果
1. Todo
2. TodoList
3.TodoItem
4.TodoInput
5. App组件引入Todo组件
一 核心代码
1.reducer
// 新增列表数据和改变数组数据
// 将业务逻辑拆分到一个单独文件中,方便进行状态管理
import _ from 'lodash';
export interface StateProps {
id: number;
text: string;
isFinished: boolean;
}
export interface ActionProps {
type: string;
[key: string]: any;
}
interface IStateObjectProps {
pickerArr: StateProps[];
filterTag: 'SHOW_ALL'|'SHOW_FINISHED'|'SHOW_NOT_FINISH';
dispatch: any;
}
const reducer = (state: IStateObjectProps, action: ActionProps) => {
console.log(state, action, 'reducer11111');
const pickerArr0 = _.get(state, 'pickerArr')||[];
switch (_.get(action, 'type')) {
case "ADD":
return {
...state,
pickerArr: [...pickerArr0, _.get(action, 'todo')]
};
case "CHANGESTATUS":
const pickerArr = _.map(pickerArr0, (item) => {
if (item.id === action.id) {
return Object.assign({}, item, { isFinished: !_.get(item, 'isFinished') });
}
return item;
})||[];
return {
...state,
pickerArr,
}
case 'SET_VISIBILITY_FILTER':
const filterTag = action.filterTag;
return {
...state,
filterTag,
};
default:
return state || {};
}
};
export default reducer
2.store.js
import React from 'react';
import reducer from './reducer'
// compose执行顺序就是从后往前
const compose = (...funcs) => {
if (funcs.length === 0) return arg => arg
return funcs.reduce((v, cur) => (...args) => (v(cur(...args))))
}
function applyMiddleware(...args) {
// 将中间件们变成一个数组
const middlewares = Array.from({ length: args.length }, (_, _key) => args[_key]);
return function (createStore) {
return function () {
const store = createStore.apply(void 0, arguments);
const middlewareAPI = {
getState: store.getState,
dispatch: store.dispatch,
};
// map遍历中间件, 执行监听器函数, 形成新数组
const chain = middlewares.map((middleware) => middleware(middlewareAPI));
// 展开中间件,调整执行顺序,并传入store.dispatch
const dispatch = compose(...chain)(store.dispatch);
// 返回需要的存储数据(将dispatch合并进store对象)
return {
...store,
dispatch,
};
};
};
}
function legacy_createStore(reducer, preloadedState) {
let state = preloadedState || null;
const listeners = [];
const subscribe = (fn) => listeners.push(fn);
const getState = () => state;
const dispatch = (action) => {
const state1 = reducer(state, action);
state = state1
// 因为是在获取到最新的state的值之后有执行的监听回调, 所以使用store.subscribe可以监听到最新的state的值!!!
listeners.forEach((fn) => fn());
return state
}
return { getState, dispatch, subscribe }
}
function createStore(reducer, preloadedState, enhancer) {
if (typeof preloadedState === 'function') {
return preloadedState(legacy_createStore)(reducer)
}
if (typeof enhancer === 'function') {
return enhancer(legacy_createStore)(reducer, preloadedState)
}
return legacy_createStore(reducer, preloadedState)
}
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument)
}
return next(action)
}
}
const thunk = createThunkMiddleware('xxxxxx');
const store = applyMiddleware(thunk)(createStore)(reducer);
// 或者
// const store = createStore(reducer, applyMiddleware(thunk));
// 或者
// const store = createStore(reducer);
console.log(store, 'oldState======')
store.subscribe(() => {
const newState = store.getState()
// 数据可能变化,需要监听最新的
console.log(newState, 'newState====');
})
export default store;
二 关于context API的使用
1. MyContext
import React from "react";
const MyContext = React.createContext({});
export default MyContext
2. createContext
import React, {ReactNode, memo} from "react";
// 已经使用了React.createContext, 这个可以忽略, 只是为了展示createContext功能的简单代码
const createContext = ({}) => {
let value = {};
const Provider = memo((props: {
children: ReactNode;
value:{
dispatch: (arg1:any)=>void;
getState:() => any;
};
}) => {
value = props.value;
return <>{props.children}</>;
});
const Consumer = memo(({ children }: { children: any }) => {
return <>{typeof children === "function" ? children(value) : children}</>;
});
return { Provider, Consumer };
};
export default createContext;
3. ContextProvider
import React, { useState } from "react";
import MyContext from "./MyContext";
import _ from "lodash";
import store from "./store";
const useReducer = (state0, dispatch0) => {
const [state, dispatch] = useState(state0);
const dispatch1 = (action) => {
dispatch0(action);
dispatch(store.getState());
};
return [state, dispatch1]
}
// 父组件
const ContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(store.getState(), store.dispatch);
return <MyContext.Provider value={{
getState: () => state,
dispatch
}}>{children}</MyContext.Provider>;
};
export default ContextProvider;
4. connect
import React from "react";
import MyContext from "./MyContext";
import _ from "lodash";
// 模拟react-redux的 connect高阶函数
const connect = (mapStateToProps, mapDispatchToProps) => {
return (Component) => (props) =>
wrapper(Component, { mapStateToProps, mapDispatchToProps, ...props });
};
const wrapper = (Comp, props) => {
const { mapStateToProps, mapDispatchToProps, ...rest } = props;
return (
<MyContext.Consumer>
{(store) => {
const dispatchs = mapDispatchToProps(_.get(store, 'dispatch'));
let states1 = mapStateToProps(_.get(store, 'getState') ? _.get(store, 'getState')(): {});
return <Comp {...{ ...states1, ...dispatchs, ...rest }} />;
}}
</MyContext.Consumer>
);
};
export default connect;
三 组件验证效果
1. Todo
import React from "react";
import TodoInput from "./TodoInput";
import TodoList from "./TodoList";
import ContextProvider from "./ContextProvider";
// 父组件
const Todo = () => {
return (
<ContextProvider>
<TodoInput />
<TodoList />
</ContextProvider>
);
};
export default Todo;
2. TodoList
import React, { useEffect } from "react";
import TodoItem from "./TodoItem";
import _ from "lodash";
import connect from "./connect";
import { mapStateTotProps } from "./mapStateToProps";
import { mapDispatchToProps } from "./mapDispatchToProps";
import styles from './TodoList.scss'
const TodoList = (props) => {
const { todoList } = props;
console.log(styles, 'TodoList-styles', props)
return (
<>
<p className={styles.title}>checckbox-list: </p>
<div className="todo-list">
{_.map(todoList, (item) => (
<TodoItem key={_.get(item, "id")} todo={item || {}} />
))}
</div>
<hr />
</>
);
};
export default connect(mapStateTotProps, mapDispatchToProps)(TodoList);
3.TodoItem
import _ from 'lodash';
import React from "react";
import connect from './connect';
import { mapStateTotProps } from "./mapStateToProps";
import { mapDispatchToProps } from "./mapDispatchToProps";
// 孙子组件
const TodoItem = (props: any) => {
const { todo, changeTodo } = props;
// 改变事项状态
const handleChange = () => {
changeTodo(_.get(todo, 'id'));
}
return (
<div className="todo-item">
<input type="checkbox" checked={todo.isFinished} onChange={handleChange} />
<span style={{ textDecoration: _.get(todo, 'isFinished') ? 'line-through' : 'none' }}>{todo.text}</span>
</div>
)
}
export default connect(mapStateTotProps, mapDispatchToProps)(TodoItem);
4.TodoInput
import React, { useState } from "react";
import connect from './connect';
import { mapStateTotProps } from "./mapStateToProps";
import { mapDispatchToProps } from "./mapDispatchToProps";
import styles from './TodoInput.scss'
// 子组件
const TodoInput = (props) => {
// console.log(styles, 'styles', 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={styles["todo-input"]}>
<input
type="text"
placeholder="请输入代办事项"
onChange={handleChangeText}
value={text}
className="aaa"
/>
<button className={styles.btn} onClick={handleAddTodo}>+添加</button>
<button className={styles.btn} onClick={showAll}>show all</button>
<button className={styles.btn} onClick={showFinished}>show finished</button>
<button className={styles.btn} onClick={showNotFinish}>show not finish</button>
</div>
);
};
export default connect(mapStateTotProps, mapDispatchToProps)(TodoInput);