redux-thunk 和在应用程序架构中 - 只想在视图中呈现视图并在单独的组件中分派 GET 操作 [英] redux-thunk and in app architecture - want to render only views in views and dispatch GET actions in separate component

查看:53
本文介绍了redux-thunk 和在应用程序架构中 - 只想在视图中呈现视图并在单独的组件中分派 GET 操作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在我的应用程序中使用了 react-redux 和 redux-thunk,我正在尝试做两件事:

  1. 我希望能够在两个组件中共享 GET 请求的结果.我知道你可以通过将两个组件连接到商店来做到这一点,但我想这样做,如果用户登陆 X 页面,那么 Y 页面不能再次发出相同的 GET 请求(这两个组件是缩略图和轮播).换句话说,GET 请求应该发出一次(不是 100% 确定 redux-thunk 的最佳实践是什么),并且每个组件都应该能够访问 store 并在组件中呈现结果(这很容易,而且我可以)

  2. 目前 GET 请求是两个子视图组件的父级,这(我认为)没有意义.我只想在父视图中呈现子视图组件,而不是 GET 请求.如果不清楚,如果您阅读下面的代码会更有意义

这是父视图 (Gallery),它有一个子组件,该组件将操作分派到 redux(使用 redux-thunk),该组件生成 API(FetchImages):

导入...导出默认函数 Gallery() {返回(<><图库标签/><图库文本/><div className="gallery-images-container"><获取图像/>---->这是发出 API 请求并渲染两个子视图组件

</>)}

这是 FetchImages,它正在分派进行 API 调用的动作 (fetchImages)

导入...函数 FetchImages({ fetchImages, imageData }) {useEffect(() => {fetchImages()}, [])返回 imageData.loading ?(<h2>加载</h2>) : imageData.error ?(<h2>出了点问题{imageData.error}</h2>) : (<><缩略图/>----->这两个是在 GET 请求成功时呈现的视图<轮播/></>)}const mapStateToProps = state =>{返回 {图像数据:state.images}}const mapDispatchToProps = dispatch =>{返回 {fetchImages: () =>调度(获取图像())}}导出默认连接(mapStateToProps,mapDispatchToProps)(获取图像)

我认为拥有这样的东西更有意义:

导入...导出默认函数 Gallery() {返回(<><图库标签/><图库文本/><div className="gallery-images-container"><缩略图/>----->缩略图应该在此处呈现而不是轮播(此处的 FetchImages 增加了不必要的复杂性)

</>)}

tldr

  1. 如果两个组件可以分派发出 GET 请求的操作,但每次用户访问网站时只能分派一次,则应遵循哪些最佳实践?

  2. 使用 redux-thunk,有哪些分离关注点的最佳实践,以便子视图组件位于父视图组件内,以及在子视图组件之间共享的更智能的组件(例如分派发出 GET 请求的操作)当用户在没有视图和更智能的组件直接放在一起的情况下登陆页面时分派?

我是菜鸟,感谢您的帮助

解决方案

你的第一个问题:你的组件容器应该只是调度它需要数据的动作.您应该如何在状态中存储异步结果并稍后处理来自状态的结果是本答案中未涵盖的内容,但后面的示例使用名为 List 的组件,该组件仅分派获取数据页面、选择数据页面并在 UI 中转储数据页面.如果数据已经处于状态,tunk 操作会提前返回.

在生产应用程序中,您可能希望存储带有加载、错误、请求和一堆额外信息的异步​​ API 结果,而不是假设它存在或不存在.

第一个答案部分回答了您的第二个问题.组件容器应该只发送一个动作,表明它们需要数据,而不必知道数据已经在那里,已经被请求或任何这些东西.

您可以使用以下代码对返回承诺的函数进行分组:

//稍后解析一个promiseconst later = (time, result) =>新承诺((解决)=>setTimeout(() => resolve(result), time));//组promise返回函数const createGroup = (缓存) =>(fn,getKey = (...x) =>JSON.stringify(x)) =>(...args) =>{const key = getKey(args);让结果 = cache.get(key);如果(结果){返回结果;}//没有缓存结果 = Promise.resolve(fn.apply(null, args)).then((r) =>{cache.resolved(key);//告诉缓存承诺完成返回 r;},(e) =>{cache.resolve(key);//告诉缓存承诺完成返回 Promise.reject(e);});cache.set(key, result);返回结果;};//永久内存缓存存储创建者const createPermanentMemoryCache = (cache = new Map()) =>{返回 {得到:(键)=>cache.get(key),设置:(键,值)=>缓存设置(键,值),已解决:(x) =>x,//在promise解析后不会移除缓存条目};};//承诺完成后的临时内存缓存存储创建者//缓存键被移除const createTmpMemCache = () =>{const map = new Map();const cache = createPermanentMemoryCache(map);cache.resolved = (key) =>map.delete(key);返回缓存;};//测试返回一个promise的函数const testPromise = (m) =>{console.log(`test promise was called with ${m}`);稍后返回(500,米);};const PermanentCache = createPermanentMemoryCache();const groupTestPromise = createGroup(permanentCache)(测试承诺,//注意这会导致对分组函数的所有调用//无论参数是什么,都存储在键 'p' 下//通过的是.在后面的 List 示例中,我忽略了这一点//并且具有不同参数的调用的保存方式不同() =>'p');Promise.all([//这使用永久缓存,其中所有调用该函数//保存在同一个键下所以 testPromise 函数//只调用一次groupTestPromise('p1'),//这将创建一个使用的promise//在所有其他调用中groupTestPromise('p2'),]).then((结果) => {console.log('第一个结果:', result);返回 Promise.all([//第一次调用后不再调用testPromise函数//解析,因为解析后没有删除缓存键//这些调用只返回相同的承诺//groupTestPromise('p1') 返回groupTestPromise('p3'),groupTestPromise('p4'),]);}).then((result) => console.log('second result', result));const tmpCache = createTmpMemCache();const tmpGroupTestPromise = createGroup(tmpCache)(测试承诺,//所有对 testPromise 的调用都使用相同的键保存//无论传递什么参数() =>'p');Promise.all([//这使用一个临时缓存,其中所有调用该函数//保存在同一个键下所以 testPromise 函数//被调用两次,t2 调用返回之前的承诺//使用 t1 调用创建,因为没有使用参数//保存结果tmpGroupTestPromise('t1'),//这里调用一次tmpGroupTestPromise('t2'),//这里没有调用t1的结果]).then((结果) => {console.log('tmp first result:', result);返回 Promise.all([//在此处使用 t3 调用一次,因为缓存键已删除//当承诺解决时tmpGroupTestPromise('t3'),tmpGroupTestPromise('t4'),//返回t3的结果]);}).then((结果) =>console.log('tmp second result', result));const tmpUniqueKeyForArg = createGroup(createTmpMemCache())(测试承诺//没有传递key函数,表示创建了缓存key//基于传递的参数);Promise.all([//这使用一个临时缓存,其中所有调用函数//根据参数保存在键下tmpUniqueKeyForArg('u1'),//这里调用tmpUniqueKeyForArg('u2'),//这里调用(u2是不同的参数)tmpUniqueKeyForArg('u1'),//这里没有调用(已经用u1调用了)tmpUniqueKeyForArg('u2'),//这里没有调用(已经用u2调用了)]).then((结果) => {console.log('唯一的第一个结果:', result);返回 Promise.all([tmpUniqueKeyForArg('u1'),//用u1调用tmp缓存移除key//承诺完成后tmpUniqueKeyForArg('u3'),//用u3调用tmpUniqueKeyForArg('u3'),//未调用,参数相同]);}).then((结果) =>console.log('唯一的第二个结果', 结果));

现在我们有代码来对返回 promise 的函数进行分组(使用相同的参数再次调用时不会调用函数),我们可以尝试将其应用于 thunk 动作创建者.

因为主干动作创建者不是 (...args)=>result 而是 (...args)=>(dispatch,getState)=>result 我们不能将动作创建者直接传递给 createGroup 我创建的 createGroupedThunkAction 采用函数分组从 (...args)=>(dispatch,getState)=>result([args],dispatch,getState)=>result 同时仍然返回具有正确签名的函数:(...args)=>(dispatch,getState)=>结果.

这是示例片段:

const { Provider, useDispatch, useSelector } = ReactRedux;const { createStore, applyMiddleware, compose } = Redux;const { createSelector } = 重新选择;//稍后解析一个promiseconst later = (time, result) =>新承诺((解决)=>setTimeout(() => resolve(result), time));//组promise返回函数const createGroup = (缓存) =>(fn,getKey = (...x) =>JSON.stringify(x)) =>(...args) =>{const key = getKey(args);让结果 = cache.get(key);如果(结果){返回结果;}//没有缓存结果 = Promise.resolve(fn.apply(null, args)).then((r) =>{cache.resolved(key);//告诉缓存承诺完成返回 r;},(e) =>{cache.resolve(key);//告诉缓存承诺完成返回 Promise.reject(e);});cache.set(key, result);返回结果;};//thunk 动作创建者不是 (...args)=>result 而是//(...args)=>(dispatch,getState)=>result//所以这里是我们如何对 thunk 动作进行分组const createGroupedThunkAction = (thunkAction, cache) =>{const 组 = 创建组(缓存)((args, dispatch, getState) =>thunkAction.apply(null, args)(dispatch, getState));返回 (...args) =>(调度,getState) =>{返回组(参数,调度,getState);};};//永久内存缓存存储创建者const createPermanentMemoryCache = (cache = new Map()) =>{返回 {得到:(键)=>cache.get(key),设置:(键,值)=>缓存设置(键,值),已解决:(x) =>x,//在promise完成后不会移除缓存条目};};常量初始状态 = {数据: {},};//动作类型const MAKE_REQUEST = 'MAKE_REQUEST';const SET_DATA = 'SET_DATA';//动作创建者const setData = (data, page) =>({类型:SET_DATA,有效载荷:{数据,页面},});const makeRequest = (页面) =>({类型:MAKE_REQUEST,有效载荷:页面,});//标准的thunk动作返回一个promiseconst getData = (page) =>(调度,getState) =>{console.log('获取页面调用的数据:',page);如果 (createSelectDataPage(page)(getState())) {返回;//如果有数据,什么都不做}//在发送任何东西之前返回一个promise返回 Promise.resolve().然后(() =>dispatch(makeRequest(page))//只有一次).then(() =>之后(500,[1, 2, 3, 4, 5, 6].slice((页 - 1) * 3,(页 - 1) * 3 + 3))).then((data) => dispatch(setData(data, page)));};//getData thunk action 作为一个分组函数const groupedGetData = createGroupedThunkAction(getData,//没有 getKey 函数所以参数被用作缓存键createPermanentMemoryCache());const reducer = (state, { type, payload }) =>{console.log('action:', JSON.stringify({ type, payload }));如果(类型 === SET_DATA){const { 数据,页面 } = 有效载荷;返回 {...状态,数据:{ ...state.data,[页面]:数据},};}返回状态;};//选择器const selectData = (状态) =>状态数据;const createSelectDataPage = (page) =>createSelector([selectData], (data) => data[page]);//使用 redux 开发工具创建 storeconst composeEnhancers =窗口.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ||撰写;const store = createStore(减速器,初始状态,composeEnhancers(应用中间件(//提供的thunk middlere({ dispatch, getState }) =>(下一个) =>(动作) =>{if (typeof action === '函数') {返回动作(调度,getState);}返回下一个(动作);})));//List 是一个使用 React.memo 的纯组件const List = React.memo(function ListComponent({ page }) {const selectDataPage = React.useMemo(() =>创建选择数据页(页面),[页]);const data = useSelector(selectDataPage);const dispatch = useDispatch();React.useEffect(() => {如果(!数据){调度(groupedGetData(页面));}}, [数据、调度、页面]);返回 (<div><pre>{数据}</pre>

);});const App = () =>(<div><列表页={1}/><列表页={1}/><列表页={2}/><列表页={2}/>

);ReactDOM.render(<提供者商店={商店}><应用程序/></提供者>,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><div id="root"></div>

在该示例中,渲染了 4 个 List 组件,两个用于第 1 页,两个用于第 2 页.所有 4 个组件都将分派 groupedGetData(page) 但如果您检查 redux 开发工具(或控制台) 您看到 MAKE_REQUEST 并且结果 SET_DATA 只分派两次(一次用于第 1 页,一次用于第 2 页)

具有永久内存缓存的相关分组函数少于 50 行,可以在这里找到

I am using react-redux and redux-thunk in my application and there are two things I am trying to do:

  1. I want to be able to share the results of a GET request in two components. I know you can do this by connecting the two components to the store, but I want to make it so if the user lands on X page, then Y page cannot make the same GET request again (these two components are Thumbnail and Carousel). In other words, the GET request should be made once (not 100% sure what best practice is here for redux-thunk), and each component should be able to access the store and render the results in the component (this is easy and I can do)

  2. currently the GET request is the parent of the two children view components, which (I think) doesn't make sense. I only want to render a child view component in the parent view, not a GET request. If unclear it will make more sense if you read my code below

This is parent view (Gallery), which has a child component which dispatches an action to redux (using redux-thunk) that makes an API (FetchImages):

import ...

export default function Gallery() {

  return(
    <>

      <GalleryTabs />
      <GalleryText />

      <div className="gallery-images-container">
        <FetchImages /> ----> this is making an API request and rendering two child view components
      </div>

    </>
  )  
}

This is FetchImages, which is dispatching the action (fetchImages) which makes the API call

import ...

function FetchImages({ fetchImages, imageData }) {

  useEffect(() => {
    fetchImages()
  }, [])

    return imageData.loading ? (
      <h2>Loading</h2>
    ) : imageData.error ? (
      <h2>Something went wrong {imageData.error}</h2>
    ) : (
      <>
      <Thumbnail /> -----> these two are views that are rendered if GET request is successful
      <Carousel />
      </>
    )
}

const mapStateToProps = state => {
    return {
        imageData: state.images
    }
}

const mapDispatchToProps = dispatch => {  
    return {
        fetchImages: () => dispatch(fetchImages())
    }
}

export default connect(
  mapStateToProps, 
  mapDispatchToProps
  )(FetchImages)

I think it makes more sense to have something like this:

import ...

export default function Gallery() {

  return(
    <>

      <GalleryTabs />
      <GalleryText />

      <div className="gallery-images-container">
        <Thumbnail />  -----> Thumbnail should be rendered here but not Carousel ( FetchImages here adds unnecessary complexity )   
      </div>

    </>
  )  
}

tldr

  1. What are some best practices to follow if two components can dispatch an action which makes a GET request but the dispatch should only be made once per time the user is on the website?

  2. Using redux-thunk, what are some best practices for separating concerns so that children view components are within parent view components and the smarter components which are shared between children view components (such as dispatching actions that make GET requests) are dispatched when the user lands on the page without the views and smarter components being directly together?

I'm a noob so thank you for any help

解决方案

Your first question: your component container should just dispatch the action that it needs data. How you should store async result in state and later handle result from state is something not covered in this answer but the later example uses a component named List that just dispatches getting a data page, selects the data page and dumps the data page in UI. The tunk action does an early return if the data is already in state.

In production application you probably want to store async api result with loading, error, requested and a bunch of extra info instead of assuming it is there or not there.

Your second question is partly answered by the first answer. Component containers should just dispatch an action indicating they need data and not have to know about the data already being there, already being requested or any of that stuff.

You can group functions that return a promise with the following code:

//resolves a promise later
const later = (time, result) =>
  new Promise((resolve) =>
    setTimeout(() => resolve(result), time)
  );
//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;
};
//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,//will not remove cache entry after promise resolves
  };
};
//temporary memory cache store creator when the promise is done
//  the cache key is removed
const createTmpMemCache = () => {
  const map = new Map();
  const cache = createPermanentMemoryCache(map);
  cache.resolved = (key) => map.delete(key);
  return cache;
};

//tesgting function that returns a promise
const testPromise = (m) => {
  console.log(`test promise was called with ${m}`);
  return later(500, m);
};
const permanentCache = createPermanentMemoryCache();
const groupTestPromise = createGroup(permanentCache)(
  testPromise,
  //note that this causes all calls to the grouped function to
  //  be stored under the key 'p' no matter what the arguments
  //  passed are. In the later List example I leave this out
  //  and calls with different arguments are saved differently
  () => 'p'
);
Promise.all([
  //this uses a permanent cache where all calls to the function
  //  are saved under the same key so the testPromise function
  //  is only called once
  groupTestPromise('p1'),//this creates one promise that's used
                         //  in all other calls
  groupTestPromise('p2'),
])
  .then((result) => {
    console.log('first result:', result);
    return Promise.all([
      //testPromise function is not called again after first calls
      //  resolve because cache key is not removed after resolving
      //  these calls just return the same promises that
      //  groupTestPromise('p1') returned
      groupTestPromise('p3'),
      groupTestPromise('p4'),
    ]);
  })
  .then((result) => console.log('second result', result));
const tmpCache = createTmpMemCache();
const tmpGroupTestPromise = createGroup(tmpCache)(
  testPromise,
  //all calls to testPromise are saved with the same key
  //  no matter what arguments are passed
  () => 'p'
);
Promise.all([
  //this uses a temporary cache where all calls to the function
  //  are saved under the same key so the testPromise function
  //  is called twice, the t2 call returns the promise that was
  //  created with the t1 call because arguments are not used
  //  to save results
  tmpGroupTestPromise('t1'),//called once here
  tmpGroupTestPromise('t2'),//not called here using result of t1
])
  .then((result) => {
    console.log('tmp first result:', result);
    return Promise.all([
      //called once here with t3 becuase cache key is removed
      //  when promise resolves
      tmpGroupTestPromise('t3'),
      tmpGroupTestPromise('t4'),//result of t3 is returned
    ]);
  })
  .then((result) =>
    console.log('tmp second result', result)
  );
const tmpUniqueKeyForArg = createGroup(createTmpMemCache())(
  testPromise
  //no key function passed, this means cache key is created
  //  based on passed arguments
);
Promise.all([
  //this uses a temporary cache where all calls to the function
  //  are saved under key based on arguments
  tmpUniqueKeyForArg('u1'), //called here
  tmpUniqueKeyForArg('u2'), //called here (u2 is different argument)
  tmpUniqueKeyForArg('u1'), //not called here (already called with u1)
  tmpUniqueKeyForArg('u2'), //not called here (already called with u2)
])
  .then((result) => {
    console.log('unique first result:', result);
    return Promise.all([
      tmpUniqueKeyForArg('u1'), //called with u1 tmp cache removes key
                                // after promise is done
      tmpUniqueKeyForArg('u3'), //called with u3
      tmpUniqueKeyForArg('u3'), //not called, same argument
    ]);
  })
  .then((result) =>
    console.log('unique second result', result)
  );

Now that we have code to group functions that return promises (function is not called when called again with same argument) we can try to apply this to thunk action creators.

Because a trunk action creator is not (...args)=>result but (...args)=>(dispatch,getState)=>result we can't pass the action creator directly to createGroup I created createGroupedThunkAction that adopts the function to group from (...args)=>(dispatch,getState)=>result to ([args],dispatch,getState)=>result while still returning a function with the right signature: (...args)=>(dispatch,getState)=>result.

Here is the example snippet:

const { Provider, useDispatch, useSelector } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;
const { createSelector } = Reselect;

//resolves a promise later
const later = (time, result) =>
  new Promise((resolve) =>
    setTimeout(() => resolve(result), time)
  );
//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,//will not remove cache entry after promise is done
  };
};
const initialState = {
  data: {},
};
//action types
const MAKE_REQUEST = 'MAKE_REQUEST';
const SET_DATA = 'SET_DATA';
//action creators
const setData = (data, page) => ({
  type: SET_DATA,
  payload: { data, page },
});
const makeRequest = (page) => ({
  type: MAKE_REQUEST,
  payload: page,
});
//standard thunk action returning a promise
const getData = (page) => (dispatch, getState) => {
  console.log('get data called with page:',page);
  if (createSelectDataPage(page)(getState())) {
    return; //do nothing if data is there
  }
  //return a promise before dispatching anything
  return Promise.resolve()
    .then(
      () => dispatch(makeRequest(page)) //only once
    )
    .then(() =>
      later(
        500,
        [1, 2, 3, 4, 5, 6].slice(
          (page - 1) * 3,
          (page - 1) * 3 + 3
        )
      )
    )
    .then((data) => dispatch(setData(data, page)));
};
//getData thunk action as a grouped function
const groupedGetData = createGroupedThunkAction(
  getData,//no getKey function so arguments are used as cache key
  createPermanentMemoryCache()
);
const reducer = (state, { type, payload }) => {
  console.log('action:', JSON.stringify({ type, payload }));
  if (type === SET_DATA) {
    const { data, page } = payload;
    return {
      ...state,
      data: { ...state.data, [page]: data },
    };
  }
  return state;
};
//selectors
const selectData = (state) => state.data;
const createSelectDataPage = (page) =>
  createSelector([selectData], (data) => data[page]);
//creating store with redux dev tools
const composeEnhancers =
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
  reducer,
  initialState,
  composeEnhancers(
    applyMiddleware(
      //improvided thunk middlere
      ({ dispatch, getState }) => (next) => (action) => {
        if (typeof action === 'function') {
          return action(dispatch, getState);
        }
        return next(action);
      }
    )
  )
);
//List is a pure component using React.memo
const List = React.memo(function ListComponent({ page }) {
  const selectDataPage = React.useMemo(
    () => createSelectDataPage(page),
    [page]
  );
  const data = useSelector(selectDataPage);
  const dispatch = useDispatch();
  React.useEffect(() => {
    if (!data) {
      dispatch(groupedGetData(page));
    }
  }, [data, dispatch, page]);
  return (
    <div>
      <pre>{data}</pre>
    </div>
  );
});
const App = () => (
  <div>
    <List page={1} />
    <List page={1} />
    <List page={2} />
    <List page={2} />
  </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>


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

In that example there are 4 List components rendered, two for page 1 and two for page 2. All 4 will dispatch groupedGetData(page) but if you check the redux dev tools (or the console) you see MAKE_REQUEST and resulting SET_DATA is only dispatched twice (once for page 1 and once for page 2)

Relevant grouping functions with permanent memory cache is less than 50 lines and can be found here

这篇关于redux-thunk 和在应用程序架构中 - 只想在视图中呈现视图并在单独的组件中分派 GET 操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆