React(w / react-redux)缓存选项

时间:2018-02-22 10:24:15

标签: reactjs caching redux react-redux

我正在构建一个用户登录的应用,并且可以看到许多统计信息。

每个统计信息都是API调用的结果 - 想象一个包含多个列的表,每列包含一个stat(只是一个数字)。

我注意到每次重新渲染组件时,都会再次进行API调用。这有很大的性能问题,因为显示stat可能需要几毫秒。我如何:

a)缓存此信息,使其保持不变,不需要在每次渲染时重新调用, b)让app"知道"何时重新调用API,因为stat已在数据库中更新?

我目前正在使用Redux来存储更明显的内容,例如用户正在查看的广告系列,但肯定有更好的方法来缓存这些统计信息,而不是为每个人创建操作和缩减器?

3 个答案:

答案 0 :(得分:3)

a)要将您的数据缓存到localStorage,请通过 Dan Abramov (redux的作者)观看此video

b)为避免重新使用您的组件,请使用 shouldComponentUpdate

shouldComponentUpdate(nextProps, nextState) {
  /**If no change in state return false*/
  return this.state.value != nextState.value;
}

通过这种方式,你可以停止不必要的重新渲染。

答案 1 :(得分:1)

本质上,您需要处理redux reducer中的所有提取操作。这就是为什么(在实现了几次缓存之后)我决定发布一个库(redux-cached-api-middleware),该库专门用于在这种情况下提供帮助(它是redux-api-middleware之上的一个薄包装)。您只需要为每个请求考虑唯一的缓存键。

这是一个示例组件,该组件从API提取项目并使用10分钟的缓存策略(这意味着如果您在缓存有效时尝试调用API,它将仅从缓存中返回数据):

import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import api from 'redux-cached-api-middleware';
import Items from './Items';
import Error from './Error';

class ExampleApp extends React.Component {
  componentDidMount() {
    this.props.fetchData();
  }

  render() {
    const { result } = this.props;
    if (!result) return null;
    if (result.fetching) return <div>Loading...</div>;
    if (result.error) return <Error data={result.errorPayload} />;
    if (result.successPayload) return <Items data={result.successPayload} />;
    return <div>No items</div>;
  }
}

ExampleApp.propTypes = {
  fetchData: PropTypes.func.isRequired,
  result: PropTypes.shape({}),
};

const CACHE_KEY = 'GET/items';

const enhance = connect(
  state => ({
    result: api.selectors.getResult(state, CACHE_KEY),
  }),
  dispatch => ({
    fetchData() {
      return dispatch(
        api.actions.invoke({
          method: 'GET',
          headers: { Accept: 'application/json' },
          endpoint: 'https://my-api.com/items/',
          cache: {
            key: CACHE_KEY,
            strategy: api.cache
              .get(api.constants.CACHE_TYPES.TTL_SUCCESS)
              .buildStrategy({ ttl: 10 * 60 * 1000 }), // 10 minutes
          },
        })
      );
    },
  })
);

export default enhance(ExampleApp);

库的设置如下:

import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { apiMiddleware } from 'redux-api-middleware';
import api from 'redux-cached-api-middleware';
import reducers from './reducers';

const store = createStore(
  combineReducers({
    ...reducers,
    [api.constants.NAME]: api.reducer,
  }),
  applyMiddleware(thunk, apiMiddleware)
);

答案 2 :(得分:0)

因为我使用的是Redux,所以答案并不像我希望的那么简单。使用上面的答案,我找到了解决方案。

首先,当组件安装时,它执行一个API调用,然后激活一个动作,然后是reducer,然后更新商店。

其次,我正在使用shouldComponentUpdate这样:

shouldComponentUpdate(nextProps){
    if(nextProps.value){
        return true
    }
    if(this.props.value){
        return false;
    }
    return true;
}

如果组件有nextProps,则重新渲染。如果它已经有值,则不要重新渲染,如果它没有(有道具)渲染。

我仍然使用componentDidMount()调用API,每次使用组件时都会有效地监听API,而且应该通过shouldComponentUpdate()方法决定渲染(或不渲染)。