簡體   English   中英

使用反應鈎子和上下文從 REST api 獲取數據以進行 state 管理的最佳方法?

[英]Best way to fetch data from a REST api using react hooks and context for state management?

我正在嘗試使用反應鈎子和上下文 API 進行 state 管理。我已經根據待辦事項應用程序中的一些代碼實現了縮減器模式,但現在我想開始定期從 API 獲取數據(例如實現無限滾動),並且我我現在不確定代碼中進行這些 async-REST-api 調用的最佳位置。

我習慣使用 redux 中間件庫,如redux-observableredux-thunk等來處理異步任務。 但是現在我沒有使用 redux,我不清楚進行異步更新的最佳方法是什么。 我想我可以使用await-promise reducers,但感覺不對。

有什么建議么? (在實現了一個 reducer 模式之后,我很想回退到一個完整的 redux-with-redux-obnservable 實現,盡管我希望上下文能夠精簡所有這些樣板文件。)

這可能是我實現它的方式。 我有一個標准的減速機。 我還將創建一個輔助功能組件來幫助我為我的上下文提供程序設置值。

我還在源代碼中做了一些注釋。 我希望以下代碼片段足夠簡單以供遵循。

    import React, { useReducer, useEffect, createContext } from 'react';
    import FetchService from './util/FetchService'; // some helper functions


    const OrderInfoContext = createContext();

    const reducer = (state, action) => {
      switch (action.type) {
        case 'init':
          return {};
        case 'changeData':
          return action.payload;
        default:
          return state;
      }
    };

    const changeData = data => ({
      type: 'changeData',
      payload: data
    });

    /**
     * This is a helper component that generate the Provider wrapper
     */
    function OrderInfoProvider(props) {
      // We will persist API payload in the state so we can assign it to the Context
      const [orders, dispatch] = useReducer(reducer, {});

      // We use useEffect to make API calls.
      useEffect(() => {
        async function fetchData() {
          /**
           * This is just a helper to fetch data from endpoints. It can be done using
           * axios or similar libraries
           */
          const orders = await FetchService
            .get('/api/orders');
          dispatch(changeData(orders))
        }
        fetchData();
      }, []);

      /**
       * we create a global object that is available to every child components
       */
      return <OrderInfoContext.Provider value={[orders, dispatch]} {...props} />;
    } 

    // Helper function to get Context
    function useOrderInfo() {
      const context = useContext(OrderInfoContext);
      if (!context) {
        throw new Error('useOrderInfo must be used within a OrderInfoProvider');
      }
      return context;
    }

    export { OrderInfoProvider, useOrderInfo , changeData }; 


這是一個使用 context 和 useReducer hook 來設置應用程序狀態和狀態和調度的上下文提供程序的示例。

容器使用useContext來獲取狀態和調度函數, useEffect來做副作用,就像你在使用 redux 時使用 thunk、saga 或中間件一樣,useMemo 將狀態映射到 props 和useCallback將每個自動調度的 action 映射到 props (我假設您熟悉 react redux connect。

import React, {
  useEffect,
  useContext,
  useReducer,
  useCallback,
  useMemo,
} from 'react';

//store provider
const Store = React.createContext();
const initStoreProvider = (rootReducer, initialState) => ({
  children,
}) => {
  const [state, dispatch] = useReducer(
    rootReducer,
    initialState
  );
  return (
    <Store.Provider value={{ state, dispatch }}>
      {children}
    </Store.Provider>
  );
};
//container for component
const ComponentContainer = ({ id }) => {
  const { state, dispatch } = useContext(Store);
  const num = state.find((n, index) => index === id);
  //side effects, asynchonously add another one if num%5===0
  //this is your redux thunk
  const addAsync = num % 5 === 0;
  useEffect(() => {
    if (addAsync)
      Promise.resolve().then(dispatch({ type: 'add', id }));
  }, [addAsync, dispatch, id]);
  //use callback so function does not needlessly change and would
  //trigger render in Component. This is mapDispatch but only for
  //one function, if you have more than one then use 
  //useCallback for each one
  const add = useCallback(
    () => dispatch({ type: 'add', id }),
    [dispatch, id]
  );
  //This is your memoized mapStateToProps
  const props = useMemo(() => ({ counter: num, id }), [
    num,
    id,
  ]);

  return (
    <Component add={add} doNothing={dispatch} {...props} />
  );
};
//use React.memo(Component) to avoid unnecessary renders
const Component = React.memo(
  ({ id, add, doNothing, counter }) =>
    console.log('render in component', id) || (
      <div>
        <button onClick={add}>{counter}</button>
        <button onClick={doNothing}>do nothing</button>
      </div>
    )
);
//initialize the store provider with root reducer and initial state
const StoreProvider = initStoreProvider(
  (state, action) =>
    action.type === 'add'
      ? state.map((n, index) =>
          index === action.id ? n + 1 : n
        )
      : state,
  [1, 8]
);

//using the store provider
export default () => (
  <StoreProvider>
    <ComponentContainer id={0} />
    <ComponentContainer id={1} />
  </StoreProvider>
);

例子在這里

https://resthooks.io/ 隨心所欲地使用 flux pattern,它允許中間件、可調試性等。但是,您不必編寫數千行 state 管理,您只需要一個簡單的聲明性數據定義

const getTodo = new RestEndpoint({
  urlPrefix: 'https://jsonplaceholder.typicode.com',
  path: '/todos/:id',
});

function TodoDetail({ id }: { id: number }) {
  const todo = useSuspense(getTodo, { id });
  return <div>{todo.title}</div>;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM