使用 useReducer 在两个组件之间共享状态

Biz*_*MAN 4 reactjs

尝试将状态从一个组件共享到另一个组件:可以从主组件访问状态,但从新组件访问时状态未定义 这是我的减速器:

export const  tableReducer=(state = [], action)=> {
    switch (action.type) {
        case 'SELECTED_LIST':
            state = JSON.parse(JSON.stringify(action.payload));
            return state;
        default:
            return state
    }
}
Run Code Online (Sandbox Code Playgroud)

从不同的文件访问它:

const [userList, usersDispatch] = useReducer(tableReducer, []);

useEffect(() => {
            const list = Object.keys(selectedRowIds).length > 0 ? selectedFlatRows.map(
                    d => d.original.email
                )
             : '';
            usersDispatch({ type: 'SELECTED_LIST', payload: list, });
    
        }, [selectedRowIds, selectedFlatRows]);
Run Code Online (Sandbox Code Playgroud)

并在一个新组件中:

const [userList] = useReducer(tableReducer);

    const deleteUsers = () => {
        console.log(userList)
    }
Run Code Online (Sandbox Code Playgroud)

但这里的console.log(userList)结果是undefined

You*_*saf 8

您无法useReducer像您尝试的那样与 hook 共享状态。每次调用都会useReducer返回一个新状态,该状态使用传递给useReducerhook 的减速器函数进行管理。

正如每次调用都会useState返回不同的状态一样,每次调用都会useReducer返回不同的状态。两个useReducer调用不能共享相同的状态。

要共享状态,您可以使用以下选项之一:


Gan*_*zal 8

对于组件之间的状态共享,您可以使用带有 useReducer 的 Context API。

Context API 提供了一种简洁的方式来为子组件提供状态,而不会以道具钻取情况结束。它要求 aProvider被设置,它将其值提供给它的任何Consumers. 作为 的子组件的任何组件都Provider可以使用上下文。

首先创建一段上下文。

自定义上下文.js

import React from 'react';

const CustomContext = React.createContext();

export function useCustomContext() {
  return React.useContext(CustomContext);
}

export default CustomContext;
Run Code Online (Sandbox Code Playgroud)

我们可以在单独的文件中定义您的减速器。

TableReducer.js

export const  tableReducer=(state = [], action)=> {
    switch (action.type) {
        case 'SELECTED_LIST':
            state = JSON.parse(JSON.stringify(action.payload));
            return state;
        default:
            return state
    }
}
Run Code Online (Sandbox Code Playgroud)

接下来是实现提供者,并在“父”组件(更高的组件)中给它一个值

import CustomContext from './CustomContext'
import { tableReducer } from './TableReducer'

const ParentComponent = () => {

  const [userState, usersDispatch ] = React.useReducer(tableReducer, []);

  const providerState = {
    userState,
    usersDispatch
  }

  return (
    <CustomContext.Provider value={providerState} >
      <ChildComponent /> //Any component within here can access value by using useCustomContext();
    </CustomContext.Provider>
  )
}
Run Code Online (Sandbox Code Playgroud)

现在,任何嵌套在 <CustomContext.Provider></CustomContext.Provider> 中的组件都可以访问传递Provider给上下文状态和调度方法的“value”属性的任何内容。

子组件看起来像这样(我已经省略了你的状态值等等......)

import { useCustomContext }from './CustomContext'

const ChildComponent = (props) => {

    //your custom state variables and other methods


    const { userState, usersDispatch } = useCustomContext();

    useEffect(() => {
        const list = Object.keys(selectedRowIds).length > 0 ? selectedFlatRows.map(
                d => d.original.email
            )
         : '';
        usersDispatch({ type: 'SELECTED_LIST', payload: list, });

    }, [selectedRowIds, selectedFlatRows]);

  return(
      <div>your components dependent on selectedRowIds, selectedFlatRows<div>
   )

}
Run Code Online (Sandbox Code Playgroud)


Thu*_*3eR 6

@Gandzal 是正确的,但我发现它缺少打字稿版本,而且今天 createContext 需要一个默认参数。这是谷歌上最热门的答案之一,所以我想我会分享。

我这样设置我的解决方案:

自定义上下文

import React, {Dispatch} from 'react';
import {StateType, Action} from './reducer'

interface IContextProps {
  state: StateType;
  dispatch:Dispatch<Action>
}

const CustomContext = React.createContext({} as IContextProps);

export function useCustomContext() {
  return React.useContext(CustomContext);
}

export default CustomContext;
Run Code Online (Sandbox Code Playgroud)

注意 StateType 和 Action:

export type StateType = {
  items: Array<DataItems>;
  date: Date;
};

export type Action = {
  type: ActionKind;
  payload: DataItems;
};
Run Code Online (Sandbox Code Playgroud)

减速器:

export const reducer = (state: StateType, action: Action) => {
  const { type, payload } = action;
  let newArray: Array<DataItems> = [];
   switch (type) {
    case ActionKind.Checked:
      newArray = state.items.map((item) => ({
        ...item,
        checked: item.id === payload.id ? true : item.checked,
      }));
      return {
        ...state,
        items: newArray,
      }

    default:
      return state;
  }
};
Run Code Online (Sandbox Code Playgroud)

应用程序.tsx:

import { reducer, initalState } from 'Shared/Reducer/reducer';
import CustomContext from 'Shared/Reducer/CustomContext';

const App: React.FC = () => {
  const [state, dispatch] = React.useReducer(reducer, initalState);

  const providerState = {
    state,
    dispatch,
  };

  return (
    <CustomContext.Provider value={providerState}>
      <main role="main">
       // your components
      </main>
    </CustomContext.Provider>
  );
};

export default App;
Run Code Online (Sandbox Code Playgroud)

还有你的组件之一:

import { useCustomContext } from 'Shared/Reducer/CustomContext';

    export const MyComp: React.FC<MyType> = (props) => {
      const { data} = props;
      const { state, dispatch } = useCustomContext(); --- Your state and dispatch here
      return (
        <div>
          // your component
        </div >
      );
    }
Run Code Online (Sandbox Code Playgroud)