闽公网安备 35020302035485号
<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。代码并不复杂、这里就不多解释了、希望大家好好阅读一下。