对redux的探究

203 阅读2分钟

最近在研究react源码,顺便再研究一下 redux ,分享一下我的学习成果,让我们来实现一个简易的redux吧。

先给大家演示一下 redux 的最基础使用吧。

import React from 'react';
import { createStore } from 'redux';
import { connect, Provider } from 'react-redux';

const initState = {
  couter: 0,
};

const reducer = (state = initState, action) => {
  const { couter } = state;
  switch (action.type) {
    case 'INCREMENT':
      return { couter: couter + 1 };
    case 'DECREMENT':
      return { couter: couter - 1 };
    default:
      return state;
  }
};

const store = createStore(reducer);

const App = () => {
  return (
    <div>
      <Provider store={store}>
        <One />
      </Provider>
    </div>
  );
};

const One = props => {
  return (
    <div>
      <Tow />
    </div>
  );
};

const mapStateToProps = state => {
  return {
    ...state,
  };
};
const mapDispatchToProps = dispatch => {
  return { dispatch };
};

const Tow = connect(
  mapStateToProps,
  mapDispatchToProps,
)(props => {
  const { couter, dispatch } = props;
  const increment = () => {
    dispatch({ type: 'INCREMENT' });
  };
  return (
    <div>
      <button onClick={increment}>{couter}</button>
    </div>
  );
});

export default App;

这份代码想必大家都已经很熟悉了,我们就从这开始揭开redux的神秘面纱。

Provider

定义一个上下文,这个简单,大致实现逻辑代码。

const ReactRedux = React.createContext(null);
const Provider = props => {
  const { store } = props;
  return (
    <ReactRedux.Provider value={store}>{props.children}</ReactRedux.Provider>
  );
};
connect

用于将全局状态数据注入到组件中,先实现其阉割版让大家理解一下。其背后的原理。(其中 store 大家为从 Provider 中获取的 store , 它拥有 dispatch 和 getState() 属性。)

const connect = (mapStateToProps, mapDispatchToProps) => Component => props => {
  const store = useContext(ReactRedux);
  let stateProps = mapStateToProps
    ? mapStateToProps(store.getState(), props)
    : {};
  let dispathProps = mapDispatchToProps
    ? mapDispatchToProps(store.dispatch, props)
    : {};
  let allProps = { ...stateProps, ...dispathProps, ...props };
  return <Component {...allProps} />;
};

从上面可以看出 connect 只是将state和dispatch通过组件传值的方式将数据传递进来。是不是很简单。

createStore

在实现 createStore 之前我们先将几个实例。 以下是一段普通的js代码

const initState = {
  couter: 0,
  color : 'red'
};

const renderApp = state => {
  renderText(state.couter);
  renderColor(state.color);
};
const renderText = (text)=>{
  document.body.innerHTML = `<div>${text}</div>`;
}
const renderColor = (color)=>{
  document.body.style.backgroundColor = color;
}
renderApp(initState);
initState.couter++;
initState.color = '#fff';



这个例子中我们可以看见,渲染之后即使我们改变数据也视图并为改变。于是我们对其进行改良。

const initState = {
  couter: 0,
  color: 'red',
};

const renderApp = state => {
  renderText(state.couter);
  renderColor(state.color);
};
const renderText = text => {
  document.body.innerHTML = `<div>${text}</div>`;
};
const renderColor = color => {
  document.body.style.backgroundColor = color;
};

const dispatch = action => {
  switch (action.type) {
    case 'couterinc':
      initState.couter++;
      renderApp(initState);
      break;
    case 'colorChang':
      initState.color = action.color;
      renderApp(initState);
      break;
  }
};

renderApp(initState);
dispatch({ type: 'couterinc' });
dispatch({ type: 'colorChang', color: '#fff' });

只需在每次改变数据之后进行重新渲染,让视图更新。这个例子中我们了解到,数据与视图直接其实就是个发布订阅的关系。于是我们将代码进行再次优化并真正实现createStore。

const createStore = reducer => {
  let state;
  const listeners = [];
  const subscribe = listener => listeners.push(listener);
  const getState = () => state;
  const dispatch = action => {
    state = reducer(state, action);
    listeners.forEach(listener => listener());
  };
  dispatch({});
  return { getState, dispatch, subscribe };
};

export default createStore;


其中 subscribe 将会在connect 中被调用, 用于重新渲染页面。 connect 完成版

const connect = (mapStateToProps, mapDispatchToProps) => Component => props => {
  const [allProps, setAllProps] = useState({});
  const store = useContext(ReactRedux);

  useEffect(() => {
    const update = () => {
      let stateProps = mapStateToProps
        ? mapStateToProps(store.getState(), props)
        : {};
      let dispathProps = mapDispatchToProps
        ? mapDispatchToProps(store.dispatch, props)
        : {};
      setAllProps({ ...stateProps, ...dispathProps, ...props });
    };
    update();

    store.subscribe(() => update());
  }, []);

  return <Component {...allProps} />;
};

在这里他订阅了一个 update 方法用于更新视图。 到此为止我们就实现了一个简易版的 redux。