使用 createContext 和 useReducer 替换 Redux

React 及其依赖版本需要升级到 v16.8.0 以上

我们以全局消息为例, 实现这个功能

创建 Store

Redux 类似,创建一个全局的 Store ,

store.js

import React, {createContext, useReducer} from 'react';
// Store 默认值
const defaultValue = {
    message: {open: false, variant: 'success', content: ``, direction: 1000},
};
// 创建 Store
export const Store = createContext(defaultValue);
const reducer = (state, action) => {
    switch(action.type) {
        case 'message':
            return {...state, message: action.payload};
        default:
            return state
    }
}
// 作为父组件挂在Store
const StoreProvider = props => {
    const [store, dispatch] = useReducer(reducer, defaultValue);
    return (
        <Store.Provider value={{...store, dispatch}}>
            {props.children}
        </Store.Provider>
    );
};
export default StoreProvider;

挂载Store

index.js 中挂载 Store

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import Store from './store'
ReactDOM.render(
  <Store>
      <App/>
  </Store>,
  document.getElementById('root')
);

挂载生产者,消费者

将消息组件 message 作为 消费者 。挂载到 React 的根组件 App.js
发送消息的组件 Content 作为 生产者 作为同级挂载到同级的叶子组件上(即 React 的根组件 App.js)。

尽量不要把 生产者(dispatch) 和 消费者 挂载到同一个组件树中,这样可能会导致无限更新,触发栈溢出异常 Maximum call stack size exceeded...

App.js

import React from 'react';
import Content from "./content"
import Message from "./message";
export default function App() {
  return (
    <React.Fragment>
      <Content/>
      <Message/>
    </React.Fragment>
  );
}

生产者

content,js

import React, {useEffect} from 'react';
import {Store} from "../../utils/store";
const Content = () => {
    const {dispatch} = React.useContext(Store);
    useEffect(() => {
            dispatch({
                type: 'message',
                payload: {open: true, content: `This is a test Message`}
            })
        }
     }, []);
    return null
}
export default Content;

消费者

消费者有两种消费模式 useContentStore.Consumer

  1. 使用 useContent 消费
    message.js
import React, {useContext} from 'react';
import Snackbar from '@material-ui/core/Snackbar';
import SnackbarContent from '@material-ui/core/SnackbarContent';
import {Store} from "../utils/store";

const Message = () => {
   //获取全局的message对象
  const {message} = useContext(Store);
  const {content, open} = message;
  return (
    <Snackbar open={open}>
      <SnackbarContent message={content}/>
    </Snackbar>
  );
}
export default Message;

2.使用 Store.Consumer 消费
message.js

import React from 'react';
import Snackbar from '@material-ui/core/Snackbar';
import SnackbarContent from '@material-ui/core/SnackbarContent';
import {Store} from "../utils/store";

const Message = () => {
  return (
     <Store.Consumer>
     { 
       ({message}) =>     
       <Snackbar open={message.open}>
            <SnackbarContent message={message.content}/>
       </Snackbar>
     }
     </Store.Consumer>
  );
}
export default Message;

一个真实世界的Demo

一个真实世界的Demo: https://jansora.com

一个真实世界的Demo 的源代码: https://github.com/Jansora/pancake

评论栏