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

时间:2020-06-27 21:20:26

标签: reactjs typescript react-redux react-hooks redux-reducers

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

  • 显示一个微调框(在加载期间)
  • 从远程服务器检索信息
  • 显示信息并删除微调器

此方法是使用useReduceruseContext进行模拟还原,如本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中的逻辑,但是微调器从不显示(正确检索和显示数据)。

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

1 个答案:

答案 0 :(得分:1)

我在您发布的代码中看不到任何可能引起您描述的问题的东西,也许在reducer中进行console.log来查看发生了什么。

我确实有一个建议,可以通过使用某种重击动作并将魔术字符串替换为常量来更改代码并将逻辑从组件中移出并移至动作中:

//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>