React:使用 useReducer 和 useContext 异步检索信息

React : retrieve info async with useReducer and useContext

我正在尝试重现我用 Reactjs/Redux/redux-thunk 做的事情:

方法是使用 useReduceruseContext 模拟 redux,如 tutorial 中所述。对于异步部分,我依赖 redux-thunk,但我不知道 useReducer 是否有任何替代方案。这是我的代码:

组件本身:

  const SearchForm: React.FC<unknown> = () => {
  const { dispatch } = React.useContext(context);
  // Fetch information when clickin on button
  const getAgentsInfo = (event: React.MouseEvent<HTMLElement>) => {
    const fetchData:() => Promise<void> = async () => {
      fetchAgentsInfoBegin(dispatch);           //show the spinner
      const users = await fetchAgentsInfo();    // retrieve info  
      fetchAgentsInfoSuccess(dispatch, users);  // show info and remove spinner
    };
    fetchData();
  }
  return (
   ...
  )

数据获取器文件:

export const fetchAgentsInfo:any = () => {
  const data = await fetch('xxxx');
  return await data.json();
};

动作文件:

export const fetchAgentsInfoBegin = (dispatch:any) => {
  return dispatch({ type: 'FETCH_AGENTS_INFO_BEGIN'});
};

export const fetchAgentsInfoSuccess = (dispatch:any, users:any) => {
  return dispatch({
    type: 'FETCH_AGENTS_INFO_SUCCESS',
    payload: users,
  });
};

export const fetchAgentsInfoFailure = (dispatch:any) => {
  return dispatch({
    type: 'FETCH_AGENTS_INFO_FAILURE'
  })
};

还有我的商店本身:

import React, { createContext, useReducer } from 'react';
import {
  ContextArgs, 
  ContextState, 
  ContextAction
} from './types';

// Reducer for updating the store based on the 'action.type'
const Reducer = (state: ContextState, action: ContextAction) => {
  switch (action.type) {
    case 'FETCH_AGENTS_INFO_BEGIN':
      return { 
        ...state,
        isLoading:true,
      };
    case 'FETCH_AGENTS_INFO_SUCCESS':
      return { 
        ...state,
        isLoading:false,
        agentsList: action.payload,
      };
    case 'FETCH_AGENTS_INFO_FAILURE':
      return { 
        ...state,
        isLoading:false,
        agentsList: [] };
    default:
      return state;
  }
};

const Context = createContext({} as ContextArgs);

// Initial state for the store
const initialState = {
  agentsList: [],
  selectedAgentId: 0,
  isLoading:false,
};

export const ContextProvider: React.FC = ({ children }) => {
  const [state, dispatch] = useReducer(Reducer, initialState);
  const value = { state, dispatch };
  Context.displayName = 'Context';
  return (
    <Context.Provider value={value}>{children}</Context.Provider>
  );
};

export default Context;

我试图部分重用此 article 中的逻辑,但从未显示微调器(正确检索和显示数据)。

我们将不胜感激您的帮助! 谢谢

我在您发布的代码中没有看到任何可能导致您描述的问题的内容,也许在减速器中执行 console.log 以查看发生了什么。

我确实有一个建议,通过使用一种 thunk 操作并将魔术字符串替换为常量来更改代码并将逻辑移出组件并移入操作中:

//action types
const BEGIN = 'BEGIN',
  SUCCESS = 'SUCCESS';
//kind of thunk action (cannot have getState)
const getData = () => (dispatch) => {
  dispatch({ type: BEGIN });
  setTimeout(() => dispatch({ type: SUCCESS }), 2000);
};
const reducer = (state, { type }) => {
  if (type === BEGIN) {
    return { ...state, loading: true };
  }
  if (type === SUCCESS) {
    return { ...state, loading: false };
  }
  return state;
};
const DataContext = React.createContext();
const DataProvider = ({ children }) => {
  const [state, dispatch] = React.useReducer(reducer, {
    loading: false,
  });
  //redux-thunk action would receive getState but
  //  cannot do that because it'll change thunkDispatch
  //  when state changes and could cause problems when
  //  used in effects as a dependency
  const thunkDispatch = React.useCallback(
    (action) =>
      typeof action === 'function'
        ? action(dispatch)
        : action,
    []
  );
  return (
    <DataContext.Provider
      value={{ state, dispatch: thunkDispatch }}
    >
      {children}
    </DataContext.Provider>
  );
};
const App = () => {
  const { state, dispatch } = React.useContext(DataContext);
  return (
    <div>
      <button
        onClick={() => dispatch(getData())}
        disabled={state.loading}
      >
        get data
      </button>
      <pre>{JSON.stringify(state, undefined, 2)}</pre>
    </div>
  );
};
ReactDOM.render(
  <DataProvider>
    <App />
  </DataProvider>,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>


<div id="root"></div>