如何在react / redux中记住HTTP请求页码

发布时间:2020-07-07 12:30

如果我已经第二次调用请求,我该如何fetchMagazineListDetail函数修饰记忆 这个参数存在,我不想发送请求。

export const fetchMagazineListDetail = (id, paginate) => {
     return dispatch => {
        dispatch(fetchMagazineListDetailBegin());
         fetch(`http://localhost:3003/magazine/${paginate}`)
        .then(res => res.json())
        .then(res => {
            if(res.error) {
                throw(res.error);
            }
            dispatch(fetchMagazineListDetailSuccess(res));
            return res;
        })
        .catch(error => {
            dispatch(fetchMagazineListDetailError(error));
        })
    }
}

Thanks in advance
回答1

您可以对操作进行分组,而不必在已分派操作时将其分派。根据缓存的不同,您可以在渲染期间,应用程序活动期间或本地存储中保留结果:

const { Provider, useDispatch, useSelector } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;
const { createSelector } = Reselect;
const { useEffect, useMemo, useState } = React;
const { produce } = immer;

//group promise returning function
const createGroup = (cache) => (
  fn,
  getKey = (...x) => JSON.stringify(x)
) => (...args) => {
  const key = getKey(args);
  let result = cache.get(key);
  if (result) {
    return result;
  }
  //no cache
  result = Promise.resolve(fn.apply(null, args)).then(
    (r) => {
      cache.resolved(key); //tell cache promise is done
      return r;
    },
    (e) => {
      cache.resolve(key); //tell cache promise is done
      return Promise.reject(e);
    }
  );
  cache.set(key, result);
  return result;
};
//thunk action creators are not (...args)=>result but
//  (...args)=>(dispatch,getState)=>result
//  so here is how we group thunk actions
const createGroupedThunkAction = (thunkAction, cache) => {
  const group = createGroup(
    cache
  )((args, dispatch, getState) =>
    thunkAction.apply(null, args)(dispatch, getState)
  );

  return (...args) => (dispatch, getState) => {
    return group(args, dispatch, getState);
  };
};
//permanent memory cache store creator
const createPermanentMemoryCache = (cache = new Map()) => {
  return {
    get: (key) => cache.get(key),
    set: (key, value) => cache.set(key, value),
    resolved: (x) => x,
  };
};
const NOT_REQUESTED = { requested: false };
const initialState = { data: {} };
//action types
const BEGIN = 'BEGIN';
const SUCCESS = 'SUCCESS';
//action creators
const fetchMagazineListDetail = createGroupedThunkAction(
  (id) => {
    return (dispatch) => {
      //return a promise
      return Promise.resolve().then(() => {
        dispatch({ type: BEGIN, payload: { id } });
        setTimeout(() =>
          dispatch({ type: SUCCESS, payload: { id } })
        );
      });
    };
  },
  createPermanentMemoryCache()
);

const reducer = (state, { type, payload }) => {
  if (type === BEGIN) {
    return produce(state, (draft) => {
      const { id } = payload;
      draft.data[id] = { ...state.data[id] };
      draft.data[id].loading = true;
      draft.data[id].requested = true;
    });
  }
  if (type === SUCCESS) {
    const { id } = payload;
    return produce(state, (draft) => {
      draft.data[id].loading = false;
      draft.data[id].requested = true;
      draft.data[id].result = payload;
    });
  }
  return state;
};
//selectors
const selectData = (state) => state.data;
const crateSelectDataById = (id) =>
  createSelector([selectData], (data) =>
    data[id] ? data[id] : NOT_REQUESTED
  );
//creating store with redux dev tools
const composeEnhancers =
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
  reducer,
  initialState,
  composeEnhancers(
    applyMiddleware(
      //thunk
      ({ dispatch, getState }) => (next) => (action) => {
        if (typeof action === 'function') {
          return action(dispatch, getState);
        }
        return next(action);
      }
    )
  )
);
const Data = React.memo(function Data({ id }) {
  const dispatch = useDispatch();
  const selectDataById = useMemo(
    () => crateSelectDataById(id),
    [id]
  );
  const result = useSelector(selectDataById);
  useEffect(() => {
    if (!result.requested) {
      dispatch(fetchMagazineListDetail(id));
    }
  }, [dispatch, id, result.requested]);
  return <pre>{JSON.stringify(result, undefined, 2)}</pre>;
});
const App = () => {
  const [id, setId] = useState(1);
  return (
    <div>
      <select
        value={id}
        onChange={(e) => setId(e.target.value)}
      >
        <option value={1}>1</option>
        <option value={2}>2</option>
      </select>
      <Data id={id} />
      <Data id={id} />
    </div>
  );
};

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  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>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reselect/4.0.0/reselect.min.js"></script>
<script src="https://unpkg.com/immer@7.0.5/dist/immer.umd.production.min.js"></script>
<div id="root"></div>