初始项目
使用原生的provider和context
import React, { useState, useContext } from "react";
// 创建上下文
const appContext = React.createContext(null);
export const App = () => {
const [appState, setAppState] = useState({
user: { name: "frank", age: 18 },
});
// 初始化上下文
const contextValue = { appState, setAppState };
return (
// 传递上下文
<appContext.Provider value={contextValue}>
<大儿子 />
<二儿子 />
<幺儿子 />
</appContext.Provider>
);
};
const 大儿子 = () => (
<section>
大儿子
<User />
</section>
);
const 二儿子 = () => (
<section>
二儿子
<UserModifier />
</section>
);
const 幺儿子 = () => <section>幺儿子</section>;
const User = () => {
const contextValue = useContext(appContext);
// console.log(contextValue);
return <div>User:{contextValue.appState.user.name}</div>;
};
const UserModifier = () => {
const contextValue = useContext(appContext);
const { appState, setAppState } = contextValue;
// const {appState, setAppState} = useContext(appContext)
// console.log(contextValue)
// console.log(appState)
const onChange = (e) => {
appState.user.name = e.target.value;
// 修改上下文
setAppState({ ...contextValue.appState });
};
return (
<div>
<input value={contextValue.appState.user.name} onChange={onChange} />
</div>
);
};
手写reducer
上文的代码中, 直接修改了state的原始内容, 不符合规范, 新增reducer来规范state创建流程
// 请从课程简介里下载本代码
import React, { useState, useContext } from "react";
// 创建上下文
const appContext = React.createContext(null);
export const App = () => {
const [appState, setAppState] = useState({
user: { name: "frank", age: 18 },
});
// 初始化上下文
const contextValue = { appState, setAppState };
return (
// 传递上下文
<appContext.Provider value={contextValue}>
<大儿子 />
<二儿子 />
<幺儿子 />
</appContext.Provider>
);
};
const 大儿子 = () => (
<section>
大儿子
<User />
</section>
);
const 二儿子 = () => (
<section>
二儿子
<UserModifier />
</section>
);
const 幺儿子 = () => <section>幺儿子</section>;
const User = () => {
const contextValue = useContext(appContext);
// console.log(contextValue);
return <div>User:{contextValue.appState.user.name}</div>;
};
// 规范state创建流程
const reducer = (state, { type, payload }) => {
if (type === "updateUser") {
return {
...state,
user: {
...state.user,
...payload, // 其实payload就是data
},
};
} else {
return state;
}
};
const UserModifier = () => {
const contextValue = useContext(appContext);
const { appState, setAppState } = contextValue;
// const {appState, setAppState} = useContext(appContext)
// console.log(contextValue)
// console.log(appState)
const onChange = (e) => {
// appState.user.name = e.target.value;
// 修改上下文
// setAppState({ ...contextValue.appState });
// 不是直接修改,而是创建新的state
// 传入旧的state,创建新的
setAppState(
reducer(appState, {
type: "updateUser",
payload: {
name: e.target.value,
},
})
);
};
return (
<div>
<input value={contextValue.appState.user.name} onChange={onChange} />
</div>
);
};
手写dispatch
修改state需要setxxx, 如果多次, 则会在onChange函数里添加太多的set逻辑, 我们将他抽取出来, 作为一个function, 规范setState流程
// 请从课程简介里下载本代码
import React, { useState, useContext } from "react";
// 创建上下文
const appContext = React.createContext(null);
export const App = () => {
const [appState, setAppState] = useState({
user: { name: "frank", age: 18 },
});
// 初始化上下文
const contextValue = { appState, setAppState };
return (
// 传递上下文
<appContext.Provider value={contextValue}>
<大儿子 />
<二儿子 />
<幺儿子 />
</appContext.Provider>
);
};
const 大儿子 = () => (
<section>
大儿子
<User />
</section>
);
const 二儿子 = () => (
<section>
二儿子
{/* <UserModifier /> */}
<Wrapper />
</section>
);
const 幺儿子 = () => <section>幺儿子</section>;
const User = () => {
const contextValue = useContext(appContext);
// console.log(contextValue);
return <div>User:{contextValue.appState.user.name}</div>;
};
// 规范state创建流程
const reducer = (state, { type, payload }) => {
if (type === "updateUser") {
return {
...state,
user: {
...state.user,
...payload, // 其实payload就是data
},
};
} else {
return state;
}
};
// context只能在组件内部访问,则这里创建一个组件来封装dispatch的逻辑,然后通过这个组件获取context(权宜之计)
const Wrapper = () => {
const contextValue = useContext(appContext);
const { appState, setAppState } = contextValue;
// 规范setState流程
const dispatch = (action) => {
setAppState(reducer(appState, action));
};
return <UserModifier dispatch={dispatch} state={appState} />;
};
const UserModifier = ({ dispatch, state }) => {
const onChange = (e) => {
dispatch({
type: "updateUser",
payload: {
name: e.target.value,
},
});
};
return (
<div>
<input value={state.user.name} onChange={onChange} />
</div>
);
};
connect 高阶组件
高阶组件: 一个函数,接收一个组件,返回一个新组件,这个组件就是高阶组件
connect: 将组件和全局的state联系起来
// 请从课程简介里下载本代码
import React, { useState, useContext } from "react";
// 创建上下文
const appContext = React.createContext(null);
export const App = () => {
const [appState, setAppState] = useState({
user: { name: "frank", age: 18 },
});
// 初始化上下文
const contextValue = { appState, setAppState };
return (
// 传递上下文
<appContext.Provider value={contextValue}>
<大儿子 />
<二儿子 />
<幺儿子 />
</appContext.Provider>
);
};
const 大儿子 = () => (
<section>
大儿子
<User />
</section>
);
const 二儿子 = () => (
<section>
二儿子
<UserModifier x={"x"}>内容</UserModifier>
{/* <Wrapper /> */}
</section>
);
const 幺儿子 = () => <section>幺儿子</section>;
const User = () => {
const contextValue = useContext(appContext);
// console.log(contextValue);
return <div>User:{contextValue.appState.user.name}</div>;
};
// 规范state创建流程
const reducer = (state, { type, payload }) => {
if (type === "updateUser") {
return {
...state,
user: {
...state.user,
...payload, // 其实payload就是data
},
};
} else {
return state;
}
};
// 每次自己写warpper包装dispatch太麻烦,封装一个return wrapper的函数
const connent = (Component) => {
// context只能在组件内部访问,则这里创建一个组件来封装dispatch的逻辑,然后通过这个组件获取context(权宜之计)
return (props) => {
const contextValue = useContext(appContext);
const { appState, setAppState } = contextValue;
// 规范setState流程
const dispatch = (action) => {
setAppState(reducer(appState, action));
};
// 如果组件有传值,应该传递(props)
return <Component {...props} dispatch={dispatch} state={appState} />;
};
};
// 传入组件,将组件和全局state联系起来,就是connect的含义
const UserModifier = connent(({ dispatch, state, ...props }) => {
const { x, children } = props;
console.log(x);
const onChange = (e) => {
dispatch({
type: "updateUser",
payload: {
name: e.target.value,
},
});
};
return (
<div>
<div>{children}</div>
<input value={state.user.name} onChange={onChange} />
</div>
);
});
实现精准render
当前每次都是重新渲染, 把没有改变的也更新了, 浪费性能(影响其实不大, 但是太多也不好)
// 请从课程简介里下载本代码
import { update } from "lodash";
import React, { useState, useContext, useMemo, useEffect } from "react";
// 创建上下文
const appContext = React.createContext(null);
// 方案2: 将组件的state提取到外部的store
const store = {
state: {
user: { name: "frank", age: 18 },
},
setState(newState) {
// console.log(newState); 没有调用组件的setState,所以不会刷新
store.state = newState;
// 遍历监听者列表,调用用户传入的(更新)方法
store.listeners.map((fn) => fn(store.state));
},
// 监听者
listeners: [],
// 让所有使用store的组件监听store的变化,实现更新渲染
subscribe(fn) {
store.listeners.push(fn);
// 返回取消订阅的函数
return () => {
const index = store.listeners.indexOf(fn);
store.listeners.splice(index, 1);
};
},
};
export const App = () => {
// 只要改变了组件(app)的state,其返回的所有组件都会重新执行
// useMemo(() => {
// return <幺儿子 />;
// }, []); // 方案1: 使用缓存,传空数组表示以后不再更新
// const [appState, setAppState] = useState({
// user: { name: "frank", age: 18 },
// });
// 初始化上下文
// const contextValue = { appState, setAppState };
return (
// 传递上下文
<appContext.Provider value={store}>
<大儿子 />
<二儿子 />
<幺儿子 />
</appContext.Provider>
);
};
const 大儿子 = () => {
console.log("大儿子");
return (
<section>
大儿子
<User />
</section>
);
};
const 二儿子 = () => {
console.log("二儿子");
return (
<section>
二儿子
<UserModifier x={"x"}>内容</UserModifier>
{/* <Wrapper /> */}
</section>
);
};
const 幺儿子 = () => {
console.log("三儿子");
return <section>幺儿子</section>;
};
// 每次自己写warpper包装dispatch太麻烦,封装一个return wrapper的函数
const connent = (Component) => {
// context只能在组件内部访问,则这里创建一个组件来封装dispatch的逻辑,然后通过这个组件获取context(权宜之计)
return (props) => {
// 在wrapper使用state,调用更新方法,可以实现更新
// 而且wrapper包装的是用到state的组件,所以更新时不会影响其他组件
const [, update] = useState({});
// const contextValue = useContext(appContext);
const { state, setState } = useContext(appContext);
// const { appState, setAppState } = contextValue;
// 只订阅一次
useEffect(() => {
store.subscribe(() => {
update({});
});
}, []);
// 规范setState流程
const dispatch = (action) => {
// setAppState(reducer(appState, action));
setState(reducer(state, action));
// update({}); // 但是,其他使用store的无法根据store内的改变而重新渲染
};
// 如果组件有传值,应该传递(props)
return <Component {...props} dispatch={dispatch} state={state} />;
};
};
const User = connent(({ state, dispatch }) => {
// const contextValue = useContext(appContext);
// const { state } = useContext(appContext);
// console.log(state);
// console.log(contextValue);
// return <div>User:{contextValue.appState.user.name}</div>;
return <div>User:{state.user.name}</div>;
});
// 规范state创建流程
const reducer = (state, { type, payload }) => {
if (type === "updateUser") {
return {
...state,
user: {
...state.user,
...payload, // 其实payload就是data
},
};
} else {
return state;
}
};
// 传入组件,将组件和全局state联系起来,就是connect的含义
const UserModifier = connent(({ dispatch, state, ...props }) => {
const { x, children } = props;
console.log(x);
const onChange = (e) => {
dispatch({
type: "updateUser",
payload: {
name: e.target.value,
},
});
};
return (
<div>
<div>{children}</div>
<input value={state.user.name} onChange={onChange} />
</div>
);
});
redux乍现
App.jsx
// 请从课程简介里下载本代码
import React from "react";
// Uncaught SyntaxError: Unexpected token '<'
import { appContext, store, connent } from "./redux.jsx";
// 方案2: 将组件的state提取到外部的store
export const App = () => {
// 只要改变了组件(app)的state,其返回的所有组件都会重新执行
// useMemo(() => {
// return <幺儿子 />;
// }, []); // 方案1: 使用缓存,传空数组表示以后不再更新
// const [appState, setAppState] = useState({
// user: { name: "frank", age: 18 },
// });
// 初始化上下文
// const contextValue = { appState, setAppState };
return (
// 传递上下文
<appContext.Provider value={store}>
<大儿子 />
<二儿子 />
<幺儿子 />
</appContext.Provider>
);
};
const 大儿子 = () => {
console.log("大儿子");
return (
<section>
大儿子
<User />
</section>
);
};
const 二儿子 = () => {
console.log("二儿子");
return (
<section>
二儿子
<UserModifier x={"x"}>内容</UserModifier>
{/* <Wrapper /> */}
</section>
);
};
const 幺儿子 = () => {
console.log("三儿子");
return <section>幺儿子</section>;
};
const User = connent(({ state, dispatch }) => {
// const contextValue = useContext(appContext);
// const { state } = useContext(appContext);
// console.log(state);
// console.log(contextValue);
// return <div>User:{contextValue.appState.user.name}</div>;
return <div>User:{state.user.name}</div>;
});
// 传入组件,将组件和全局state联系起来,就是connect的含义
const UserModifier = connent(({ dispatch, state, ...props }) => {
const { x, children } = props;
console.log(x);
const onChange = (e) => {
dispatch({
type: "updateUser",
payload: {
name: e.target.value,
},
});
};
return (
<div>
<div>{children}</div>
<input value={state.user.name} onChange={onChange} />
</div>
);
});
redux.jsx
import React, { useState, useContext, useEffect } from "react";
// 每次自己写warpper包装dispatch太麻烦,封装一个return wrapper的函数
export const connent = (Component) => {
// context只能在组件内部访问,则这里创建一个组件来封装dispatch的逻辑,然后通过这个组件获取context(权宜之计)
return (props) => {
// 在wrapper使用state,调用更新方法,可以实现更新
// 而且wrapper包装的是用到state的组件,所以更新时不会影响其他组件
const [, update] = useState({});
// const contextValue = useContext(appContext);
const { state, setState } = useContext(appContext);
// const { appState, setAppState } = contextValue;
// 只订阅一次
useEffect(() => {
store.subscribe(() => {
update({});
});
}, []);
// 规范setState流程
const dispatch = (action) => {
// setAppState(reducer(appState, action));
setState(reducer(state, action));
// update({}); // 但是,其他使用store的无法根据store内的改变而重新渲染
};
// 如果组件有传值,应该传递(props)
return <Component {...props} dispatch={dispatch} state={state} />;
};
};
// 规范state创建流程
const reducer = (state, { type, payload }) => {
if (type === "updateUser") {
return {
...state,
user: {
...state.user,
...payload, // 其实payload就是data
},
};
} else {
return state;
}
};
export const store = {
state: {
user: { name: "frank", age: 18 },
},
setState(newState) {
// console.log(newState); 没有调用组件的setState,所以不会刷新
store.state = newState;
// 遍历监听者列表,调用用户传入的(更新)方法
store.listeners.map((fn) => fn(store.state));
},
// 监听者
listeners: [],
// 让所有使用store的组件监听store的变化,实现更新渲染
subscribe(fn) {
store.listeners.push(fn);
// 返回取消订阅的函数
return () => {
const index = store.listeners.indexOf(fn);
store.listeners.splice(index, 1);
};
},
};
export const appContext = React.createContext(null)
让connect支持selector
App.jsx
// 请从课程简介里下载本代码
import React from "react";
// Uncaught SyntaxError: Unexpected token '<'
import { appContext, store, connent } from "./redux.jsx";
// 方案2: 将组件的state提取到外部的store
export const App = () => {
return (
// 传递上下文
<appContext.Provider value={store}>
<大儿子 />
<二儿子 />
<幺儿子 />
</appContext.Provider>
);
};
// const x儿子 ......
const User = connent((state) => {
// 获取局部state,无需再state.xxx.xxx
return { user: state.user };
})(({ user }) => {
return <div>User:{user.name}</div>;
});
// 传入组件,将组件和全局state联系起来,就是connect的含义
const UserModifier = connent()(({ dispatch, state, ...props }) => {
// ......
});
redux.jsx
import React, { useState, useContext, useEffect } from "react";
// 每次自己写warpper包装dispatch太麻烦,封装一个return wrapper的函数
// 这个写法表示 先接收一个参数,再接收一个参数
export const connent = (selector) => (Component) => {
// context只能在组件内部访问,则这里创建一个组件来封装dispatch的逻辑,然后通过这个组件获取context(权宜之计)
return (props) => {
// 在wrapper使用state,调用更新方法,可以实现更新
// 而且wrapper包装的是用到state的组件,所以更新时不会影响其他组件
const [, update] = useState({});
// const contextValue = useContext(appContext);
const { state, setState } = useContext(appContext);
// const { appState, setAppState } = contextValue;
const data = selector ? selector(state) : { state: state };
// 只订阅一次
useEffect(() => {
store.subscribe(() => {
update({});
});
}, []);
// 规范setState流程
const dispatch = (action) => {
// setAppState(reducer(appState, action));
setState(reducer(state, action));
// update({}); // 但是,其他使用store的无法根据store内的改变而重新渲染
};
// 如果组件有传值,应该传递(props)
// return <Component {...props} dispatch={dispatch} state={state} />;
return <Component {...props} dispatch={dispatch} {...data} />;
};
};
// 规范state创建流程
const reducer = (state, { type, payload }) => {
// ......
};
export const store = {
// ......
};
export const appContext = React.createContext(null);
精准渲染
redux.jsx
import React, { useState, useContext, useEffect } from "react";
const changed = (oldState, newState) => {
let changed = false;
for (let key in oldState) {
if (oldState[key] !== newState[key]) {
changed = true;
}
}
return changed;
};
// 每次自己写warpper包装dispatch太麻烦,封装一个return wrapper的函数
// 这个写法表示 先接收一个参数,再接收一个参数
export const connent = (selector) => (Component) => {
// context只能在组件内部访问,则这里创建一个组件来封装dispatch的逻辑,然后通过这个组件获取context(权宜之计)
return (props) => {
// 在wrapper使用state,调用更新方法,可以实现更新
// 而且wrapper包装的是用到state的组件,所以更新时不会影响其他组件
const [, update] = useState({});
// const contextValue = useContext(appContext);
const { state, setState } = useContext(appContext);
// const { appState, setAppState } = contextValue;
const data = selector ? selector(state) : { state: state };
// 只订阅一次
useEffect(() => {
// const unsubscribe =
return store.subscribe(() => {
const newData = selector
? selector(store.state)
: { state: store.state };
// 判断data(store.state.xxx)是否发生变化
if (changed(data, newData)) {
console.log("update");
update({});
}
});
// return unsubscribe; // 取消订阅
// 这里最好取消订阅,否则selector变化时可能出现重复订阅
}, [selector]);
// 规范setState流程
const dispatch = (action) => {
// setAppState(reducer(appState, action));
setState(reducer(state, action));
// update({}); // 但是,其他使用store的无法根据store内的改变而重新渲染
};
// 如果组件有传值,应该传递(props)
// return <Component {...props} dispatch={dispatch} state={state} />;
return <Component {...props} dispatch={dispatch} {...data} />;
};
};
// 规范state创建流程
const reducer = (state, { type, payload }) => {
// ......
};
export const store = {
// ......
};
export const appContext = React.createContext(null);
App.jsx
// 请从课程简介里下载本代码
import React from "react";
// Uncaught SyntaxError: Unexpected token '<'
import { appContext, store, connent } from "./redux.jsx";
// 方案2: 将组件的state提取到外部的store
export const App = () => {
return (
// 传递上下文
<appContext.Provider value={store}>
<大儿子 />
<二儿子 />
<幺儿子 />
</appContext.Provider>
);
};
// ......
const 幺儿子 = connent((state) => {
return { group: state.group };
})(({ group }) => {
console.log("三儿子");
return (
<section>
幺儿子<div>Group: {group.name}</div>
</section>
);
});
const User = connent((state) => {
// 获取局部state,无需再state.xxx.xxx
return { user: state.user };
})(({ user }) => {
console.log('user')
return <div>User:{user.name}</div>;
});
// 传入组件,将组件和全局state联系起来,就是connect的含义
const UserModifier = connent()(({ dispatch, state, ...props }) => {
// ......
});
mapDispatcherToProps
App.jsx
// 请从课程简介里下载本代码
import React from "react";
// Uncaught SyntaxError: Unexpected token '<'
import { appContext, store, connent } from "./redux.jsx";
// 方案2: 将组件的state提取到外部的store
export const App = () => {
return (
// 传递上下文
<appContext.Provider value={store}>
<大儿子 />
<二儿子 />
<幺儿子 />
</appContext.Provider>
);
};
// ......
// 传入组件,将组件和全局state联系起来,就是connect的含义
const UserModifier = connent(null, (dispatch) => {
return {
// 每次都要传update,简化
updateUser: (attr) => dispatch({ type: "update", payload: attr }),
};
// })(({ dispatch, state, ...props }) => {
})(({ updateUser, state, ...props }) => {
const { x, children } = props;
console.log(x);
const onChange = (e) => {
updateUser({ name: e.target.value });
// dispatch({
// type: "update",
// payload: {
// name: e.target.value,
// },
// });
};
return (
<div>
<div>{children}</div>
<input value={state.user.name} onChange={onChange} />
</div>
);
});
redux.jsx
import React, { useState, useContext, useEffect } from "react";
const changed = (oldState, newState) => {
// ......
};
// 每次自己写warpper包装dispatch太麻烦,封装一个return wrapper的函数
// 这个写法表示 先接收一个参数,再接收一个参数
// export const connent = (selector,mapDispatchToProps) => (Component) => {
export const connent = (selector, dispatchSelector) => (Component) => {
// context只能在组件内部访问,则这里创建一个组件来封装dispatch的逻辑,然后通过这个组件获取context(权宜之计)
return (props) => {
// 在wrapper使用state,调用更新方法,可以实现更新
// 而且wrapper包装的是用到state的组件,所以更新时不会影响其他组件
const [, update] = useState({});
// const contextValue = useContext(appContext);
const { state, setState } = useContext(appContext);
// const { appState, setAppState } = contextValue;
const data = selector ? selector(state) : { state: state };
// 规范setState流程
const dispatch = (action) => {
// setAppState(reducer(appState, action));
setState(reducer(state, action));
// update({}); // 但是,其他使用store的无法根据store内的改变而重新渲染
};
// 每次都要传update,简化
const dispatchers = dispatchSelector
? dispatchSelector(dispatch)
: { dispatch };
// 只订阅一次
useEffect(() => {
// const unsubscribe =
return store.subscribe(() => {
const newData = selector
? selector(store.state)
: { state: store.state };
// 判断data(store.state.xxx)是否发生变化
if (changed(data, newData)) {
console.log("update");
update({});
}
});
// return unsubscribe; // 取消订阅
// 这里最好取消订阅,否则selector变化时可能出现重复订阅
}, [selector]);
// 如果组件有传值,应该传递(props)
// return <Component {...props} dispatch={dispatch} state={state} />;
// return <Component {...props} dispatch={dispatch} {...data} />;
return <Component {...props} {...dispatchers} {...data} />;
};
};
// 规范state创建流程
const reducer = (state, { type, payload }) => {
// ......
};
export const store = {
// ......
};
export const appContext = React.createContext(null);
connect的意义
多次调用, 每次调用产生半成品, 可以根据不同的半成品来进行读/写操作
connects/connectToUser.js
import { connect } from '../redux'
// 抽取公共selector和dispatcher
// 封装读逻辑
const userSelector = (state) => {
return { user: state.user };
};
// 封装写逻辑
const userDispatcher = (dispatch) => {
return {
updateUser: (attrs) => dispatch({ type: "update", payload: attrs }),
};
};
export const connentToUser = connect(userSelector, userDispatcher);
App.jsx
// 请从课程简介里下载本代码
import React from "react";
// Uncaught SyntaxError: Unexpected token '<'
import { appContext, store,connect } from "./redux.jsx";
import { connentToUser } from "./connects/connectToUser.js";
// 方案2: 将组件的state提取到外部的store
export const App = () => {
return (
// 传递上下文
<appContext.Provider value={store}>
<大儿子 />
<二儿子 />
<幺儿子 />
</appContext.Provider>
);
};
// ......
const UserModifier = connentToUser(({ updateUser, user, ...props }) => {
const { x, children } = props;
console.log(x);
const onChange = (e) => {
updateUser({ name: e.target.value });
// dispatch({
// type: "update",
// payload: {
// name: e.target.value,
// },
// });
};
return (
<div>
<div>{children}</div>
<input value={user.name} onChange={onChange} />
</div>
);
});
封装provider和createStore
redux.jsx
import React, { useState, useContext, useEffect } from "react";
// ......
// reduce和store都是写死的,不符合规范
// 规范state创建流程
// const reducer = (state, { type, payload }) => {
// if (type === "update") {
// return {
// ...state,
// user: {
// ...state.user,
// ...payload, // 其实payload就是data
// },
// };
// } else {
// return state;
// }
// };
export const store = {
// state: {
// user: { name: "frank", age: 18 },
// group: { name: "前端组" },
// },
state: undefined,
setState(newState) {
// console.log(newState); 没有调用组件的setState,所以不会刷新
store.state = newState;
// 遍历监听者列表,调用用户传入的(更新)方法
store.listeners.map((fn) => fn(store.state));
},
reducer: undefined,
// 监听者
listeners: [],
// 让所有使用store的组件监听store的变化,实现更新渲染
subscribe(fn) {
store.listeners.push(fn);
// 返回取消订阅的函数
return () => {
const index = store.listeners.indexOf(fn);
store.listeners.splice(index, 1);
};
},
};
// 创建store和reducer
export const createStore = (reducer, initState) => {
store.state = initState;
store.reducer = reducer;
return store;
};
export const appContext = React.createContext(null);
export const Provider = ({ store, children }) => {
return <appContext.Provider value={store}>{children}</appContext.Provider>;
};
App.jsx
// 请从课程简介里下载本代码
import React from "react";
// Uncaught SyntaxError: Unexpected token '<'
import { appContext, createStore, connect, Provider } from "./redux.jsx";
import { connentToUser } from "./connects/connectToUser.js";
const reducer = (state, { type, payload }) => {
if (type === "update") {
return {
...state,
user: {
...state.user,
...payload,
},
};
} else {
return state;
}
};
const initState = {
user: { name: "frank", age: 23 },
group: { name: "前端组" },
};
const store = createStore(reducer, initState);
// 方案2: 将组件的state提取到外部的store
export const App = () => {
return (
// 传递上下文
// <appContext.Provider value={store}>
// <大儿子 />
// <二儿子 />
// <幺儿子 />
// </appContext.Provider>
<Provider store={store}>
<大儿子 />
<二儿子 />
<幺儿子 />
</Provider>
);
};
// ......
const User = connentToUser(({ user }) => {
// ......
});
const UserModifier = connentToUser(({ updateUser, user, ...props }) => {
// ......
});
redux概念总结
代码重构
redux.jsx
import React, { useState, useContext, useEffect } from "react";
const changed = (oldState, newState) => {
let changed = false;
for (let key in oldState) {
if (oldState[key] !== newState[key]) {
changed = true;
}
}
return changed;
};
// 每次自己写warpper包装dispatch太麻烦,封装一个return wrapper的函数
// 这个写法表示 先接收一个参数,再接收一个参数
// export const connent = (selector,mapDispatchToProps) => (Component) => {
export const connect = (selector, dispatchSelector) => (Component) => {
// context只能在组件内部访问,则这里创建一个组件来封装dispatch的逻辑,然后通过这个组件获取context(权宜之计)
return (props) => {
// 在wrapper使用state,调用更新方法,可以实现更新
// 而且wrapper包装的是用到state的组件,所以更新时不会影响其他组件
const [, update] = useState({});
// const contextValue = useContext(appContext);
const { setState } = useContext(appContext);
// const { appState, setAppState } = contextValue;
const data = selector ? selector(state) : { state: state };
// // 规范setState流程
// const dispatch = (action) => {
// // setAppState(reducer(appState, action));
// setState(reducer(state, action));
// // update({}); // 但是,其他使用store的无法根据store内的改变而重新渲染
// };
// 每次都要传update,简化
const dispatchers = dispatchSelector
? dispatchSelector(dispatch)
: { dispatch };
// 只订阅一次
useEffect(() => {
// const unsubscribe =
return store.subscribe(() => {
const newData = selector ? selector(state) : { state };
// 判断data(store.state.xxx)是否发生变化
if (changed(data, newData)) {
console.log("update");
update({});
}
});
// return unsubscribe; // 取消订阅
// 这里最好取消订阅,否则selector变化时可能出现重复订阅
}, [selector]);
// 如果组件有传值,应该传递(props)
// return <Component {...props} dispatch={dispatch} state={state} />;
// return <Component {...props} dispatch={dispatch} {...data} />;
return <Component {...props} {...dispatchers} {...data} />;
};
};
let state = undefined;
let reducer = undefined;
// 监听者
let listeners = [];
const setState = (newState) => {
// console.log(newState); 没有调用组件的setState,所以不会刷新
state = newState;
// 遍历监听者列表,调用用户传入的(更新)方法
listeners.map((fn) => fn(state));
};
export const store = {
getState() {
return state;
},
// 规范setState流程
dispatch: (action) => {
// setAppState(reducer(appState, action));
setState(reducer(state, action));
// update({}); // 但是,其他使用store的无法根据store内的改变而重新渲染
},
// 让所有使用store的组件监听store的变化,实现更新渲染
subscribe(fn) {
listeners.push(fn);
// 返回取消订阅的函数
return () => {
const index = listeners.indexOf(fn);
listeners.splice(index, 1);
};
},
};
const dispatch = store.dispatch;
// 创建store和reducer
export const createStore = (_reducer, initState) => {
state = initState;
reducer = _reducer;
return store;
};
export const appContext = React.createContext(null);
export const Provider = ({ store, children }) => {
return <appContext.Provider value={store}>{children}</appContext.Provider>;
};