redux、react-redux、useReducer、useContext

215 阅读2分钟

分别使用reduxreact-redux来实现todoList操作。

使用useContext, useReducer, createContext来代替redux, 计数器案例

1. 安装

npm install redux react-redux --save

2. store文件夹下文件。

  • Action.js(提供各个action对象的函数),
  • ActionTypes.js(action中type常量),
  • index.js(提供访问数据),
  • Reducer.js(对数据的操作)

Snipaste_2021-03-17_21-28-54.png

// Action.js

    import { ADDITEM, DELETEITEM } from './ActionTypes'
    // 增加的action
    export const addHandle = () => {
      return {
        type: ADDITEM
      }
    }
    // 删除的action
    export const deleteHandle = () => {
      return {
        type: DELETEITEM
      }
    }

// ActionTypes.js

    export const ADDITEM = "addItem"
    export const DELETEITEM = "deleteItem"
// index.js

    import { createStore } from "redux";
    import reducer from './Reducer'

// 参数二,表示使用redux-tools插件
    const store = createStore(
    reducer, 
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
    );

    export default store;
// reducer.js
    
    import { ADDITEM, DELETEITEM } from './ActionTypes'

    let defaultState = {
      defaultValue: "我是默认值",
      list: [
        "今天天气真好啊",
        "昨天天气也好啊"
      ]
    }



    export default function reducer(state = defaultState, action) {
      if (action.type === ADDITEM) {
        let newState = JSON.parse(JSON.stringify(state))
        newState.list.push(action.value)
        return newState
      }
      if (action.type === DELETEITEM) {
        let newState = JSON.parse(JSON.stringify(state))
        newState.list.splice(action.index, 1)
        return newState
      }
      return state
    }

3. 使用redux

    import React, { Component, createRef } from 'react';
    import store from './store';
    import { addHandle, deleteHandle } from './store/Action'
    
    export default class TestRedux extends Component {
      constructor(props) {
        super(props)
        // this.state不能等于非Object的值
        this.state = store.getState();
        this.inputDom = createRef();
      }
      componentDidMount() {
        store.subscribe(() => this.setState(store.getState()))
      }
      // 增加
      addItem = () => {
        if (this.inputDom.current) {
          store.dispatch({ ...addHandle(), value: this.inputDom.current.value })
        }
      }
      // 删除
      deleteItem = (index) => {
        console.log(index)
        store.dispatch({ ...deleteHandle(), index })
      }



      render() {
        return <div>
          <input type="text" ref={this.inputDom} placeholder={this.state.defaultValue} />
          <button onClick={this.addItem}>增加</button>
          <ul>
            {
              this.state.list && this.state.list.map((item, index) => <li onClick={() => this.deleteItem(index)} key={index}>{item}</li>)
            }
          </ul>
        </div>
      }
    }

4. 使用react-redux的内置组件Provider包裹需要使用store中数据的组件

通过Provider组件(提供store)包裹的组件,都可以访问store中的值。

// App.js

    import TestRedux from './TestRedux';
    import { Provider } from 'react-redux'
    import store from "./store";
    
    <Provider store={store}>
      <TestRedux></TestRedux>
    </Provider>

5. 使用react-redux的内置函数connect包裹需要使用store中数据的组件

使用connect(stateToProps, dispatchToProps)(组件)包裹使用store的组件,该函数传递一个state映射函数和dispatch映射函数。都被映射到props上。

注意: 如果dispatch操作需要访问组件中的数据,我们可以通过事件传递参数。

// TestRedux.jsx
    import React, { createRef } from 'react';
    import { connect } from 'react-redux';
    import { addHandle, deleteHandle } from './store/Action'

     const TestRedux = (props) => {
       const inputDom = createRef();
       const { list, defaultValue, addItem, deleteItem } = props
       return (<div>
         <input type="text" ref={inputDom} placeholder={defaultValue} />
         <button onClick={() => addItem(inputDom)}>增加</button>
         <ul>
           {
             list && list.map((item, index) => <li onClick={() => deleteItem(index)} key=  {index}>{item}</li>)
           }
         </ul>
       </div>)
     }

    // 将store中得值映射到props上。
    const stateToProps = (state) => {
      return {
        defaultValue: state.defaultValue,
        list: state.list
      }
    }
    // 将事件映射到props上
    const dispatchToProps = (dispatch) => {
      return {
        // 增加
        // 如果我们需要在组件中获取到特定的值,例如:dom。我们可以通过事件传递参数。
        addItem(inputDom) {
          if (inputDom.current) {
            dispatch({ ...addHandle(), value: inputDom.current.value })
          }
        },
        // 删除
        deleteItem(index) {
          dispatch({ ...deleteHandle(), index })
        }
      }
    }
export default connect(stateToProps, dispatchToProps)(TestRedux)

6. 使用useContext和useReducer来代替redux

我们在父组件中使用createContext()来提供state和dispatch,使用useReducer(reducer, initState)来做数据处理操作

通过useContext()来获取Provider组件包裹的组件中使用state,dispatch值做出一些操作。

// UpDown.jsx-----父组件

    import React, { createContext, useReducer } from "react";

    export const countContext = createContext({});
    export const UPCOUNT = "UPCOUNT"
    export const DOWNCOUNT = "DOWNCOUNT"

    function reducer(state, action) {
      switch (action.type) {
        case UPCOUNT:
          return action.value;
        case DOWNCOUNT:
          return action.value
        default:
          return state;
      }
    }
    export default function UpDown(props) {
      const [count, dispatch] = useReducer(reducer, 0)
      return (
      // 注意:这里的value传递的是对象
        <countContext.Provider value={{ count, dispatch }}>
          {props.children}
        </countContext.Provider>
      )
    }
// Operator.js ----子组件

    import { useContext } from "react";
    import { DOWNCOUNT, countContext, UPCOUNT } from './UpDown'

    export default function Operator(props) {
      const { count, dispatch } = useContext(countContext);

      return (
        <div>
          <p>当前计数:{count}</p>
          <button onClick={() => dispatch({ type: UPCOUNT, value: count + 1 })}>增加</button>
          <button onClick={() => dispatch({ type: DOWNCOUNT, value: count - 1 })}>减少</button>
        </div>
      )
    }