<div id="app"></div> <script src="js/react.js" type="text/javascript"></script> <script src="js/react-dom.js" type="text/javascript"></script> <script src="js/react-redux.js" type="text/javascript"></script> <script src="js/redux.js" type="text/javascript"></script> <script src="js/babel.js"></script> <script type="text/babel"> const { Provider, connect } = ReactRedux; const { createStore } = Redux; const Sub = connect(state => state)((props) => { console.log('sub update...', props); return ( <div onClick={() => { props.dispatch({ type: 123 }) }}> hello, Sub! {props.age} </div> ) }) const Sub2 = connect(state => ({name: state.name}))((props) => { console.log('sub2 update...', props); return ( <div>hello, Sub2! {props.name}</div> ) }) function reducer(state, action) { return { ...state, age: 15 + Math.random() } } const store = createStore(reducer, { name: '_zhangsan_' }); const Body = (props, ref) => { return ( <Provider store={store}> <Sub /> <Sub2 /> </Provider> ) } ReactDOM.render(<Body />, document.getElementById('app'));我们要想实现react-redux、其实只要实现Provider、connect、createStore三个方法就可以了。
2.react-redux怎么知道一个组件订阅了哪些变量、例如上述代码中、Sub组件订阅了所有的变量、Sub2只订阅了name、因此更新age、Sub更新而Sub2不更新。
//堆代码 duidaima.com function myConnect(mapToState) { return function (Component) { return function (props) { const [, setUpdateObject] = useState({}); const state = store.getState(); const effectState = mapToState(state); function checkForUpdates() { var newState = store.getState(); if (newState != state) { var newEffectState = mapToState(newState); if (!shallowEqual(effectState, newEffectState)) { setUpdateObject({}); } } } useLayoutEffect(() => { var unsubscribe = store.subscribe(checkForUpdates); return unsubscribe; }, [store]); return React.createElement( Component, Object.assign({}, props, effectState, { dispatch: store.dispatch }) ); } } }简单解释下、myConnet方法返回的是第三行的匿名函数、该函数是用来渲染的我们的组件、函数里面涉及到的store是我们用createStore创建的store对象。通过useLayoutEffect在store中注册一个checkForUpdates方法:首先拿到store中的state对象、调用mapToState获取新的state、然后通过shallowEqual去对比是否有更新、如果有更新、通过setState触发父组件更新、从而更新我们的子组件。
<div id="app"></div> <script src="js/react.js" type="text/javascript"></script> <script src="js/react-dom.js" type="text/javascript"></script> <script src="js/babel.js"></script> <script type="text/babel"> const { useState, useLayoutEffect } = React; function myCreateStore(reducer, initState) { var state = initState; var listeners = []; function dispatch(action) { state = reducer(state, action); for (var i = 0; i < listeners.length; i++) { listeners[i](); } return action } dispatch({ type: '@@redux/INIT' }) return { dispatch, subscribe(cb) { var i = listeners.length; listeners.push(cb); return function () { listeners.splice(i, 1); } }, getState() { return state; } } } function myConnect(mapToState) { return function (Component) { return function (props) { const [, setUpdateObject] = useState({}); const state = store.getState(); const effectState = mapToState(state); function checkForUpdates() { var newState = store.getState(); if (newState != state) { var newEffectState = mapToState(newState); if (!shallowEqual(effectState, newEffectState)) { setUpdateObject({}); } } } useLayoutEffect(() => { var unsubscribe = store.subscribe(checkForUpdates); return unsubscribe; }, [store]); return React.createElement( Component, Object.assign({}, props, effectState, { dispatch: store.dispatch }) ); } } } const Sub = myConnect(state => state)((props) => { console.log('sub update...', props); // useLayoutEffect(() => { // props.dispatch({ type: 123 }) // }, []) return ( <div onClick={() => { props.dispatch({ type: 123 }) }}> hello, Sub! {props.age} </div> ) }) const Sub2 = myConnect(state => ({name: state.name}))((props) => { console.log('sub2 update...', props); return ( <div>hello, Sub2! {props.name}</div> ) }) function reducer(state, action) { return { ...state, age: 15 + Math.random() } } const store = myCreateStore(reducer, { name: '_zhangsan_' }); const Body = (props, ref) => { return ( <div> <Sub /> <Sub2 /> </div> ) } ReactDOM.render(<Body />, document.getElementById('app')); function shallowEqual(objA, objB) { if (is(objA, objB)) return true; if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { return false; } var keysA = Object.keys(objA); var keysB = Object.keys(objB); if (keysA.length !== keysB.length) return false; for (var 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; } function is(x, y) { if (x === y) { return x !== 0 || y !== 0 || 1 / x === 1 / y; } else { return x !== x && y !== y; } }这里直接省略了Provider、因为其作用是用来传递store对象。源码是通过React.createContext 创建一个Context、然后通过Provider来读取外界创建的store并挂载在Context中、这样内部就能通过Context来使用store。代码并不复杂、这里就不多解释了、希望大家好好阅读一下。