Open xiaochengzi6 opened 2 years ago
React-Redux将所有组将分为两大类: UI组件和容器组件
一、UI组件满足以下特征:
UI组件
1.只负责UI的呈现 2.没有状态 3.所有数据都有参数提供 4.不适用Redux的API
1.只负责UI的呈现
2.没有状态
3.所有数据都有参数提供
4.不适用Redux的API
UI 组件又称为"纯组件",即它纯函数一样,纯粹由参数决定它的值。
二、容器组件和UI组件完全相反
容器组件
1.负责管理数据和业务逻辑,不负责 UI 的呈现 2.带有内部状态 3.使用 Redux 的 API
1.负责管理数据和业务逻辑,不负责 UI 的呈现
2.带有内部状态
3.使用 Redux 的 API
UI组件负责页面的呈现。容器组件负责管理数据和逻辑。
三、Redux负责为UI提供容器组件进行状态的管理。React-Redux 提供connect方法,用于从 UI 组件生成容器组件。
connect
import { connect } from 'react-redux' const todo = <div> Hello </div> const VisibleTodo = connect()(todo)
在这里就会生成一个名为VisibleTodo的容器组件,没有往里面传入什么参数所以还没有什么实际作用。
为了让容器组件有容器组件该有的功能需要满足两方面的信息
(1)输入逻辑:外部的数据(即state对象)如何转换为 UI 组件的参数 (2)输出逻辑:用户发出的动作如何变为 Action 对象,从 UI 组件传出去。
(1)输入逻辑:外部的数据(即state对象)如何转换为 UI 组件的参数
state
(2)输出逻辑:用户发出的动作如何变为 Action 对象,从 UI 组件传出去。
四、这里开始放入计时器的部分代码
class Counter extends Component { render() { const { value, onIncreaseClick } = this.props return ( <div> <span> {value} </span> <button onClick={onIncreaseClick}>Increase</button> </div> ) } }
定义了一个Counter的组件 它是一个无状态组件它接收{value, onIncreaseClick}的参数。
//开始定义了一个Reducer function counter(state={count: 0}, action){ const count = state.count switch(action.type) { case: 'increase': return{ count: count + 1} default: return state } }
connect规定了四个参数常用的是前两个参数
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
(1). 官方定义:[mapStateToProps(state, [ownProps]): stateProps] (Function): 如果定义该参数,组件将会监听 Redux store 的变化。任何时候,只要 Redux store 发生改变,mapStateToProps 函数就会被调用。该回调函数必须返回一个纯对象,这个对象会与组件的 props 合并。如果你省略了这个参数,你的组件将不会监听 Redux store。如果指定了该回调函数中的第二个参数 ownProps,则该参数的值为传递到组件的 props,而且只要组件接收到新的 props,mapStateToProps 也会被调用
mapStateToProps(state, [ownProps]): stateProps
mapStateToProps
ownProps
理解:可以看到mapStateToProps这个参数必须是个函数,它的作用监听store是否变化,如果变化就是调用这个函数从新计算state的值
function mapstateToProps (state) { return{ value: state.count; } }
它建立了一个state对象到props的映射关系,1.它接收 state 作为参数,并返回一个对象,这个对象有一个 value 的属性它代表这UI的同名属性,也可以认为它会为UI组件的this.props.value创建一个映射关系{this.props.value === state.count}当数据变动时就会重新调用mapStateToProps函数来重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。
{this.props.value === state.count}
const UI = ({value}) => { return ( <div> {value} </div> ) } function mapstateToProps (state,ownProps) { return{ value: state.count; //这里的 value 就会传入到 UI 中 } } const Us = connect( mapstateToProps )(UI)
2.第二个参数将代表容器组件的props对象,使用ownProps作为参数后,如果组件参数变化,也会引起UI组件从新渲染
const mapStateToProps = (state, ownProps) => ( return { active: ownProps.filter === state.visibilityFilter } )
connect方法可以省略mapStateToProps参数,那样的话,UI 组件就不会订阅Store,就是说 Store 的更新不会引起 UI 组件的更新。
(2). 官方定义:mapDispatchToProps(dispatch, [ownProps]): dispatchProps(Object or Function): 如果传递的是一个对象,那么每个定义在该对象的函数都将被当作 Redux action creator,对象所定义的方法名将作为属性名;每个方法将返回一个新的函数,函数中dispatch方法会将action creator的返回值作为参数执行。这些属性会被合并到组件的 props 中。
mapDispatchToProps(dispatch, [ownProps]): dispatchProps
dispatch
理解: mapDispatchToProps 他可以做一个函数也可以做一个对象,会到dispatch和ownProps(容器组件的props对象)两个参数。
mapDispatchToProps
props
它定义了哪些用户的操作应该当作 Action,传给 Store。
const mapDispatchToProps = (dispatch,ownProps) => { return { onIncreaseClick: () => { dispatch({ type: 'ONCHANGE', value: 'Incer' }) } } } //或者 function mapDispatchToProps(dispatch) { return { onIncreaseClick: () => dispatch(increaseAction) } }
mapDispatchToProps作为函数,应该返回一个对象,该对象的每个键值对都是一个映射,定义了 UI 组件的参数怎样发出 Action。
如果mapDispatchToProps是一个对象,它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,会被当作 Action creator ,返回的 Action 会由 Redux 自动发出。
const mapDispatchToProps = { onIncreaseClick: () => { type: 'ONCHNGE', value: 'Incer' } }
const App = connect( mapStateToProps, mapDispatchToProps )(Counter)
问题来了 mapStateToProps如何获得state的呢,mapDispatchToProps是如何传递action的
connect方法生成容器组件以后,需要让容器组件拿到state对象,才能生成 UI 组件的参数。
一种解决方法是将state对象作为参数,传入容器组件。但是,这样做比较麻烦,尤其是容器组件可能在很深的层级,一级级将state传下去就很麻烦。
React-Redux 提供Provider组件,可以让容器组件拿到state。
Provider
官方定义:<Provider store> 使组件层级中的 connect() 方法都能够获得 Redux store。正常情况下,你的根组件应该嵌套在 <Provider> 中才能使用 connect() 方法。
<Provider store>
connect()
<Provider>
ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') )
这样App所有子组件就可以默认拿到state
完整例子:
import React, { Component } from 'react' // import PropTypes from 'prop-types' import ReactDOM from 'react-dom' import { createStore } from 'redux' import { Provider, connect } from 'react-redux' class Counter extends Component { render() { const { value, onIncreaseClick } = this.props return ( <div> <span> {value} </span> <button onClick={onIncreaseClick}>Increase</button> </div> ) } } //Reducer function counter(state = {count: 0}, action) { const count = state.count switch (action.type) { case 'increase': return { count: count + 1 } default: return state } } function mapStateToProps(state) { return { value: state.count } } function mapDispatchToProps(dispatch) { return { onIncreaseClick: () => dispatch(increaseAction) } } const increaseAction = { type: 'increase' } const App = connect( mapStateToProps, mapDispatchToProps )(Counter) const store = createStore(counter) ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') )
state数据的流向问题?
(默认state)Reducer --1--> (store = creactStore(Reducer) )--2--> \ --3--> (App = connect(...)(Component)) --4-->Component
第一步由 Reducer 构造初始的 state 然后在会通过 第二步 store 保存数据通过 getState() 来获取 state 通过\ 传向 APP
在之前的react-redux 的使用模式下你不需要特殊的处理 redux可以直接写在组件中 但在复杂的项目中为了保持项目中组件的纯度 你常常需要创建 store 文件夹用来管理状态
-store ├── actionCreators.js ├── constants.js ├── index.js └── reduxcer.js
不用多说这就非常麻烦了,在 constants.js 中编写静态变量 在 actionCreators.js中创建 action Function (生成函数)使用 reduxcer.js用于判断不同的 action 对数据造成的问题
constants.js
actionCreators.js
reduxcer.js
当然到这一步还没有结束,在没有使用 Hook 的情况下每一次编写的组件要明确区分是 容器组件 还是UI组件 这两者最大的不同就是接收数据的问题,前者需要操作数据,后者只是简单的使用传入的数据就行
// 省略 imort // 容器组件 function App (){ return( <div></div> ) } const mapDispatchProps = (dispatch) => { change1: (data) => { dispatch(actionFunction(data)) } } const mapStateProps = (state) => { value: state.value1 } App = connect(mapStateProps, mapDispatchProps)(React.memo(App)) export default App
redux 是以 Reducer 为主的 状态管理工具 (initState, action) => neweState 参数 InitState 是默认的值,那么通过多个 Reducer 组成的
(initState, action) => neweState
function (state={}, action){ return { value1: (state.value1, action) => newState, value2: (state.value2, action) => newState, value3: (state.value3, action) => newState, } }
由多个 Reducer 组成的函数通过 const store = createStore(Reducer) 这样的方式生成的 Store ,而 Store 可以理解为数据状态的管理中心,可以使用 dispatch({type: ''})来去修改数据,使用 subscribe 去订阅,由于每次都需要编写 带有type 属性的对象去修改数据,那么通常使用 actionCreate()函数来去返回一个对象 { type: 'VALUE', ...} 方便调用更改数据 每一次都需要编写 type 那么将常用的字段提取出来放入一个单独的文件中,那么这就是 redux 简单的使用。
const store = createStore(Reducer)
actionCreate()
{ type: 'VALUE', ...}
react-redux 在 redux 外层包裹了一层 react 组件使其能够方便的 react 使用 这里主要看一下核心 API
首先是在全局的根目录下导入 store
// ... import { Provider } from 'react-redux' ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') );
简单的看一下做了什么事情 由于和原生组件并不一样这里就简写为了了解其原理:
1、为了能够在全局中使用 store 数据这里使用到了 [context ](https://github.com/xiaochengzi6/Blog/issues/12)
const ReactReduxContext = React.createContext()
2、通过 <ReactReduxContext.provider value={}/> 组件去传递数据并订阅 context
<ReactReduxContext.provider value={}/>
3、之后开始编写 Provider 组件
import {ReactReduxContext} from './ReactReduxContext' export default function Provider (props){ const {store, children} = props const contextValue = { store } return ( <ReactReduxContext.Provider value={contextValue}> {children} </ReactReduxContext.Provider> ) }
provider 组件做的内容就是转发一下 store 以及将子组件放入合适的位置
4、react-redux 将组件分为两大类 容器组件和UI组件 容器组件需要高阶组件 connect 去包裹 如此才能将 store 数据传入 组件的 props 中,将用户的动作通过 dispatch 发出
import {ReactReduxContext} from './ReactReduxContext' export default function connect(mapStateToProps, mapDispatchToProps) { return function Componenct(WrappendComponent) { return function ConnectFunction(props) { const {...wrapperProps} = props // 获取 store 数据 const {store} = useContext(ReactReduxContext) const states = store.getState() const stateProps = mapStateToProps(state) const dispatchProps = mapDispatchToProps(store.dispatch) const propsValues = Object.assign({}, stateProps, dispatchProps, wrapperProps) return <WrappendComponent {...propsValues} /> } } }
5、这里虽然是通过 dispatch 进行更新数据 虽然数据更新但其组件并不没有随之发生改变 所以这里需要去监听数据的变动以跟新组件
1、检查当数据 state 发生变化的时候这里要去检查传给组件的 state (参数)是否一致【为什么是参数? 因为state 会和参数 props、 stateProps 以及dispatchProps 合并在一起 但最终要检查的是组合在一起的 props】 2、当参数发生改变就要重新渲染组件
1、检查当数据 state 发生变化的时候这里要去检查传给组件的 state (参数)是否一致【为什么是参数? 因为state 会和参数 props、 stateProps 以及dispatchProps 合并在一起 但最终要检查的是组合在一起的 props】
2、当参数发生改变就要重新渲染组件
将 connect 数据的获取抽离出成一个函数
function childPropsSelector (state, wrapperProps){ const states = store.getState() const stateProps = mapStateToProps(state) const dispatchProps = mapDispatchToProps(store.dispatch) return Object.assign({}, stateProps, dispatchProps, wrapperProps) }
6、在检查参数的时候 需要获得上次的渲染参数 然后和这次的渲染参数进行对比, redux 采用的是浅比较,如果使用 immer 库对数据进行包裹这样的比较会比较好,而且也不会发生深比较的循环引用问题
function is(x, y){ if(x === y){ return x !== 0 || y !== 0 || 1/x === 1/y }else{ return x !== x && y !== y } } export default function shallowEqual(objA, objB) { if (is(objA, objB)) return true if ( typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null ) { return false } const keysA = Object.keys(objA) const keysB = Object.keys(objB) if (keysA.length !== keysB.length) return false for (let i = 0; i < keysA.length; i++) { if ( !Object.prototype.hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]]) ) { return false } } return true }
修改 connect 函数的内容
import {ReactReduxContext} from './ReactReduxContext' import {shallowEqual} from './shallowEqual' export default function connect(mapStateToProps, mapDispatchToProps) { return function Componenct(WrappendComponent) { function childPropsSelector(state, wrapperProps) { const states = store.getState() const stateProps = mapStateToProps(state) const dispatchProps = mapDispatchToProps(store.dispatch) return Object.assign({}, stateProps, dispatchProps, wrapperProps) } return function ConnectFunction(props) { const { ...wrapperProps } = props // 获取 store 数据 const { store } = useContext(ReactReduxContext) const propsValues = childPropsSelector(store, wrapperProps) const lastChildProps = useRef() // 保存上一次的 参数 useEffect(() => [(lastChildProps.current = propsValues)], []) // 监听 store 是否发生变化 store.subscribe(()=>{ const newChildProps = childPropsSelector(store, wrapperProps) if(!shallowEqual(newChildProps, lastChildProps)){ lastChildProps.current = newChildProps } }) return <WrappendComponent {...propsValues} /> } } }
7、数据发生改变并且也监听到了 这个时候就要去强制更新 使用 useRedux 去派发 dispatch 从而让组件强制更新
useRedux
function storeStateUpdatesReducer(count){ return count + 1 } export default function connect(mapStateToProps, mapDispatchToProps) { //... const [, forceComponentUpdataDiaptch] = useReducer(storeStateUpdatesReducer, 0) store.subscribe(()=>{ const newChildProps = childPropsSelector(store, wrapperProps) if(!shallowEqual(newChildProps, lastchildProps)){ lastChildProps.current = newChildProps // 这里去强制组件更新 forceComponentUpdataDiaptch() } }) // 。。。 }
8、当然这里还涉及到了更新先后的问题,父组件通过 connect 获取到 redux 中的 store 进行 dispatch 改变数据,子组件也是从 redux 取出 store 从而更新 数据的更新都是派发 dispatch 每一次派发 dispatch 都会保持上一次的数据快照,如果是上面的类型显示是两次单独的数据更新【分别从 redux 中取出 store 数据】显然没有这样的先后关系 可能会引发问题,所以应该保持上一次的 store 在上次的 store 改变之后去更新组件
export class Subscription{ constructor(store, parentSub){ this.store = store this.parentSub = patentSub this.handleChangeWrapper = this.handleChangeWrapper.bind(this) } // 添加监听器 addNestedSub(listener){ this.listeners.push(listener) } // 添加子组件的回调 从而触发更新 notifyNestedSubs(){ const length = this.listeners.length for(let i = 0; i < length; i++){ const callback = this.listeners[i] callback() } } // 包装回调函数--- 目的就是为了 在 this.store 下去调用函数 handleChangeWrapper(){ if(this.onStateChange){ this.onStateChange() } } trySubScribe(){ this.parentSub ? this.parentSub.addNestedSub(this.handleChangeWrapper) : this.store.subscribe(this.handleChangeWrapper) } }
9、通过 Subscription 来去维护这样的关系 在整个项目中 只有 容器组件 或者说 被 connect()包裹的组件才有机会去使用 diapatch 更新数据 每一次的更新数据后都会改变 store 然后组件更新开始于从父级层层往下直到目前的容器组件 而每一次的更新【派发 dispatch】又都会从上一次的 store 中取到最新值,确保更新顺序
Subscription
diapatch
import Subscription from './Subscription'; export default function Provider(props){ const {store, childer} = props const contextValue = useMemo(()=>{ const subscript = new Subscript(store) // 回调事件--可以理解为数据发生改变时候要触发的事件也就是 store.subscript(listerne) 中的监听事件 listerne subscript.onStateChange = subscript.notifyNestedSubs return { store, subscript } }, [store]) const previousState = useMemo(()=> store.getState(0) ), [store]) useEffect(()=>{ const {subscription} = contextValue // 这里去往 subscript 中添加 onStateChange 函数 subscript.trySubscribe() if(previousState !== store.getState()){ // 这里发现 store 并不是之前的数据了 就会去调用之前存储的监听事件 subscription.notifyNestedSubs() } }, [contextValue, previousState]) return ( <ReactReduxContext.Provider value={contextValue}> {children} </ReactReduxContext.Provider> ) }
之后再修改一下 connect 就可以了
import React, { useContext, useRef, useLayoutEffect, useReducer } from 'react'; import ReactReduxContext from './Context'; import shallowEqual from './shallowEqual'; import Subscription from './Subscription export default function connect(mapStateToProps, mapDispatchToProps){ return function connectHoc(WrappredComponent){ function childPropsSelector(store, wrapperProps){ //... } return function connectFunctioon(props){ const {...wrappedn} = props const contextValue = useContext(ReactReduxContext) const { store, subscription: parentSub } = contextValue const actualChildProps = childPropsSelector(store, wrapperProps) // 保存上一次的 props const lastChildProps = useRef() useLayoutEffect(()=>{ lastChildProps.currnt = actualChildProps }, [actualChildProps]) // 创建一个 reducer 用来强制更新组件 const [,forceComponentUpdateDispatch] = useReducer(storeStateUpdatesReducer, 0) // 创建 subscription 实例用于确保组件更新的顺序 const subscription = new Subscript(store, parentSub) const checkForUpdates = () =>{ const newChildProps = childPropsSelector(store, wrapperProps) if(!shallowEqual(newChildProps, lastChildProps)){ lastChildProps.current = newChildProps // 强制更新 forceComponentUpdateDispatch() // 通知 调用其子级为其添加的监听事件 subscription.notifyNestedSubs(); } } // 注册这次的监听事件 subscript.onStateChange = checkForUpdates // 将其放入上一次的 store 中 subscription.trySubscribe() } } }
[写的非常不错的 文章主要是去学习他的--手写一个React-Redux,玩转React的Context API][https://juejin.cn/post/6847902222756347911#comment]
React-Redux将所有组将分为两大类: UI组件和容器组件
一、
UI组件
满足以下特征:UI 组件又称为"纯组件",即它纯函数一样,纯粹由参数决定它的值。
二、
容器组件
和UI组件完全相反UI组件负责页面的呈现。容器组件负责管理数据和逻辑。
三、Redux负责为UI提供容器组件进行状态的管理。React-Redux 提供
connect
方法,用于从 UI 组件生成容器组件。在这里就会生成一个名为VisibleTodo的容器组件,没有往里面传入什么参数所以还没有什么实际作用。
为了让容器组件有容器组件该有的功能需要满足两方面的信息
四、这里开始放入计时器的部分代码
定义了一个Counter的组件 它是一个无状态组件它接收{value, onIncreaseClick}的参数。
connect规定了四个参数常用的是前两个参数
(1). 官方定义:[
mapStateToProps(state, [ownProps]): stateProps
] (Function): 如果定义该参数,组件将会监听 Redux store 的变化。任何时候,只要 Redux store 发生改变,mapStateToProps
函数就会被调用。该回调函数必须返回一个纯对象,这个对象会与组件的 props 合并。如果你省略了这个参数,你的组件将不会监听 Redux store。如果指定了该回调函数中的第二个参数ownProps
,则该参数的值为传递到组件的 props,而且只要组件接收到新的 props,mapStateToProps
也会被调用理解:可以看到mapStateToProps这个参数必须是个函数,它的作用监听store是否变化,如果变化就是调用这个函数从新计算state的值
它建立了一个state对象到props的映射关系,1.它接收 state 作为参数,并返回一个对象,这个对象有一个 value 的属性它代表这UI的同名属性,也可以认为它会为UI组件的this.props.value创建一个映射关系
{this.props.value === state.count}
当数据变动时就会重新调用mapStateToProps函数来重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。2.第二个参数将代表容器组件的props对象,使用ownProps作为参数后,如果组件参数变化,也会引起UI组件从新渲染
(2). 官方定义:
mapDispatchToProps(dispatch, [ownProps]): dispatchProps
(Object or Function): 如果传递的是一个对象,那么每个定义在该对象的函数都将被当作 Redux action creator,对象所定义的方法名将作为属性名;每个方法将返回一个新的函数,函数中dispatch
方法会将action creator的返回值作为参数执行。这些属性会被合并到组件的 props 中。理解:
mapDispatchToProps
他可以做一个函数也可以做一个对象,会到dispatch
和ownProps
(容器组件的props
对象)两个参数。它定义了哪些用户的操作应该当作 Action,传给 Store。
mapDispatchToProps
作为函数,应该返回一个对象,该对象的每个键值对都是一个映射,定义了 UI 组件的参数怎样发出 Action。如果
mapDispatchToProps
是一个对象,它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,会被当作 Action creator ,返回的 Action 会由 Redux 自动发出。问题来了 mapStateToProps如何获得state的呢,mapDispatchToProps是如何传递action的
connect
方法生成容器组件以后,需要让容器组件拿到state
对象,才能生成 UI 组件的参数。一种解决方法是将
state
对象作为参数,传入容器组件。但是,这样做比较麻烦,尤其是容器组件可能在很深的层级,一级级将state
传下去就很麻烦。React-Redux 提供
Provider
组件,可以让容器组件拿到state
。官方定义:
<Provider store>
使组件层级中的connect()
方法都能够获得 Redux store。正常情况下,你的根组件应该嵌套在<Provider>
中才能使用connect()
方法。这样App所有子组件就可以默认拿到state
完整例子:
state数据的流向问题?
第一步由 Reducer 构造初始的 state 然后在会通过 第二步 store 保存数据通过 getState() 来获取 state 通过\ 传向 APP
总结一下
在之前的react-redux 的使用模式下你不需要特殊的处理 redux可以直接写在组件中 但在复杂的项目中为了保持项目中组件的纯度 你常常需要创建 store 文件夹用来管理状态
不用多说这就非常麻烦了,在
constants.js
中编写静态变量 在actionCreators.js
中创建 action Function (生成函数)使用reduxcer.js
用于判断不同的 action 对数据造成的问题当然到这一步还没有结束,在没有使用 Hook 的情况下每一次编写的组件要明确区分是
容器组件
还是UI组件
这两者最大的不同就是接收数据的问题,前者需要操作数据,后者只是简单的使用传入的数据就行redux 是以 Reducer 为主的 状态管理工具
(initState, action) => neweState
参数 InitState 是默认的值,那么通过多个 Reducer 组成的由多个 Reducer 组成的函数通过
const store = createStore(Reducer)
这样的方式生成的 Store ,而 Store 可以理解为数据状态的管理中心,可以使用 dispatch({type: ''})来去修改数据,使用 subscribe 去订阅,由于每次都需要编写 带有type 属性的对象去修改数据,那么通常使用actionCreate()
函数来去返回一个对象{ type: 'VALUE', ...}
方便调用更改数据 每一次都需要编写 type 那么将常用的字段提取出来放入一个单独的文件中,那么这就是 redux 简单的使用。手写一个简单的 React-redux
react-redux 在 redux 外层包裹了一层 react 组件使其能够方便的 react 使用 这里主要看一下核心 API
首先是在全局的根目录下导入 store
简单的看一下做了什么事情 由于和原生组件并不一样这里就简写为了了解其原理:
1、为了能够在全局中使用 store 数据这里使用到了 [context ](https://github.com/xiaochengzi6/Blog/issues/12)
2、通过
<ReactReduxContext.provider value={}/>
组件去传递数据并订阅 context3、之后开始编写
Provider
组件provider 组件做的内容就是转发一下 store 以及将子组件放入合适的位置
4、react-redux 将组件分为两大类 容器组件和UI组件 容器组件需要高阶组件
connect
去包裹 如此才能将 store 数据传入 组件的 props 中,将用户的动作通过 dispatch 发出5、这里虽然是通过 dispatch 进行更新数据 虽然数据更新但其组件并不没有随之发生改变 所以这里需要去监听数据的变动以跟新组件
将 connect 数据的获取抽离出成一个函数
6、在检查参数的时候 需要获得上次的渲染参数 然后和这次的渲染参数进行对比, redux 采用的是浅比较,如果使用 immer 库对数据进行包裹这样的比较会比较好,而且也不会发生深比较的循环引用问题
修改 connect 函数的内容
7、数据发生改变并且也监听到了 这个时候就要去强制更新 使用
useRedux
去派发 dispatch 从而让组件强制更新8、当然这里还涉及到了更新先后的问题,父组件通过 connect 获取到 redux 中的 store 进行 dispatch 改变数据,子组件也是从 redux 取出 store 从而更新 数据的更新都是派发 dispatch 每一次派发 dispatch 都会保持上一次的数据快照,如果是上面的类型显示是两次单独的数据更新【分别从 redux 中取出 store 数据】显然没有这样的先后关系 可能会引发问题,所以应该保持上一次的 store 在上次的 store 改变之后去更新组件
9、通过
Subscription
来去维护这样的关系 在整个项目中 只有 容器组件 或者说 被connect()
包裹的组件才有机会去使用diapatch
更新数据 每一次的更新数据后都会改变 store 然后组件更新开始于从父级层层往下直到目前的容器组件 而每一次的更新【派发 dispatch】又都会从上一次的 store 中取到最新值,确保更新顺序之后再修改一下
connect
就可以了参考文章
[写的非常不错的 文章主要是去学习他的--手写一个React-Redux,玩转React的Context API][https://juejin.cn/post/6847902222756347911#comment]