【问题标题】:React redux async data sync then re-render view反应 redux 异步数据同步然后重新渲染视图
【发布时间】:2017-11-29 14:05:44
【问题描述】:

我为不同的内容类型设置了许多操作和缩减程序,例如页面、活动和场地。这些 action 和 reducer 通过另一个名为 sync 的 action 获取已保存到 AsyncStorage 的数据,并将其放入存储中。

Sync 对 Contentful 执行异步调用并检索任何新的/更新的/删除的条目,然后我将其保存到 AsyncStorage

在异步调用完成后,确保正确重新呈现视图的最佳方法是什么?

syncReducer 是否应该将通常由pagesReducervenuesReducer 等拉出的数据合并到存储中,还是应该在syncReducer 完成后发出某种事件?

数据被异步拉入以供离线查看和保持快速,所以我真的不想在渲染前等待同步。

数据/sync.js

import { AsyncStorage } from 'react-native';

import database from './database';

const cache = {
  getByType: async (query) => {
    return new Promise(async(resolve, reject) => {
      // Get results from AsyncStorage
      resolve(results);
    });
  },

  sync: async () => {
    return new Promise(async(resolve, reject) => {
      database
        .sync(options)
        .then(async results => {
          // Save results to AsyncStorage
          resolve(results);
        });
    });
  }
};

export default cache;

actions/sync.js

import actionTypes from '../constants/actionTypes';
import cache from '../data/cache';

export function sync() {
  return dispatch => {
    dispatch(syncRequestedAction());

    return cache
      .sync()
      .then(() => {
        dispatch(syncFulfilledAction());
      })
      .catch(error => {
        console.log(error);
        dispatch(syncRejectedAction());
      });

  };
}


function syncRequestedAction() {
  return {
    type: actionTypes.SyncRequested
  };
}

function syncRejectedAction() {
  return {
    type: actionTypes.SyncRejected
  };
}

function syncFulfilledAction(data) {
  return {
    type: actionTypes.SyncFulfilled,
    data
  };
}

actions/getPages.js

import actionTypes from '../constants/actionTypes';
import cache from '../data/cache';

export function getPages() {
  return dispatch => {
    dispatch(getPagesRequestedAction());

    return cache
      .getByType('page')
      .then(results => {
        dispatch(getPagesFulfilledAction(results));
      })
      .catch(error => {
        console.log(error);
        dispatch(getPagesRejectedAction());
      });

  };
}


function getPagesRequestedAction() {
  return {
    type: actionTypes.GetPagesRequested
  };
}

function getPagesRejectedAction() {
  return {
    type: actionTypes.GetPagesRejected
  };
}

function getPagesFulfilledAction(settings) {
  return {
    type: actionTypes.GetPagesFulfilled,
    pages
  };
}

reducers/pagesReducer.js

import { merge } from 'lodash';

import actionTypes from '../constants/actionTypes';

const pagesReducer = (state = {}, action) => {
  switch (action.type) {
    case actionTypes.GetPagesRequested: {
      return merge({}, state, { loading: true });
    }
    case actionTypes.GetPagesRejected: {
      return merge({}, state, { error: 'Error getting pages', loading: false });
    }
    case actionTypes.GetPagesFulfilled: {
      const merged =  merge({}, state, { error: false, loading: false });
      return { ...merged, data: action.pages };
    }
    default:
      return state;
  }
};

export default pagesReducer;

【问题讨论】:

  • 能否添加一些真实代码或伪代码来说明数据流是什么?
  • 你需要让你的组件响应prop的变化。在数据可用之前,数据同步不应更改道具。这意味着您应该在异步 redux 操作中完成所有这些操作(我更喜欢使用 sagas)。这样,一旦所有异步工作完成,它只需使用准备好的数据更新存储一次。所以你的组件真的可以是“哑巴”/“纯”的,只是基于道具渲染。
  • @markerikson 如果有帮助的话,我已经用一些精简的代码更新了我的问题。

标签: react-native react-redux redux-thunk


【解决方案1】:

最后,我能够通过将其他操作导入到我的同步操作中来解决这个问题,并根据需要更新的数据进行调度。

import { getEvents } from './getEvents';
import { getPages } from './getPages';
import { getVenues } from './getVenues';

export function sync() {
  return dispatch => {
    dispatch(syncRequestedAction());

    return cache
      .sync()
      .then(results => {
        dispatch(syncFulfilledAction());

        if (results.includes('event')) {
          dispatch(getEvents());
        }

        if (results.includes('page')) {
          dispatch(getPages());
        }

        if (results.includes('venue')) {
          dispatch(getSettings());
        }

      })
      .catch(error => {
        console.log(error);
        dispatch(syncRejectedAction());
      });

  };
}

【讨论】:

    【解决方案2】:

    您的同步操作应该是一个 thunk 函数(redux 中间件),它调用 Contentful、解析承诺并包含数据或错误。然后您可以分派另一个动作,或将数据减少到存储中的动作。

    在您想要重新渲染的每个组件上(基于通过我们刚刚调度和减少的操作在存储中更新的数据),如果您有 connect(mapStateToProps, mapDispatchToProps) 并且已将存储的这些部分包含在 MSTP 中,这些道具将被更新,这将重新渲染组件。

    如有必要,您甚至可以更明确地了解数据的解析,方法是创建另一个操作,您可以在其中分派和减少存储的某些部分当前获取状态。

    因此,当您拨打电话时,您可以分派“FETCH_IN_PROGRESS”,然后分派“FETCH_ERROR”或“FETCH_SUCCESS”,如果这是 mapStateToProps 进入您的组件,您可以选择在 shouldComponentUpdate() 中评估它,并根据在在这个过程中,您可以根据是否要重新渲染返回 true 或 false。您也可以在 componentWillReceiveProps 中强制渲染。我会首先依靠道具更改并在必要时添加它。

    【讨论】:

      【解决方案3】:

      你应该使用 Redux Persist 来处理这种事情,它支持 AsyncStorage 和一系列其他选项。

      https://github.com/rt2zz/redux-persist

      Actions 和 Reducers 应该只是为了更新 Redux 存储而设计的。任何其他操作都称为副作用,应在中间件或存储增强器中进行管理。

      我强烈建议不要使用 Redux-Thunk,因为它模糊了动作和中间件代码之间的界限,所以它对于一些有用的东西来说太强大了,而且很容易创建不可维护的反模式代码。

      如果你认为你需要使用 Redux-Thunk,请先看看他们是否已经是一个可以满足你需要的中间件,如果没有,请了解 Redux-Sagas。

      【讨论】:

        猜你喜欢
        • 2019-02-03
        • 2020-06-14
        • 1970-01-01
        • 2020-11-20
        • 2021-12-07
        • 1970-01-01
        • 2021-04-06
        • 1970-01-01
        • 2018-05-27
        相关资源
        最近更新 更多