【问题标题】:React Redux DispatchReact Redux 调度
【发布时间】:2017-01-06 21:53:46
【问题描述】:

调用 dispatch 以获取 React 组件的初始数据的最佳方式是什么。我的理解是在渲染之前调用了 ComponentWillMount。所以理论上,如果我在 ComponentWillMount 上调用 dispatch,当我点击渲染然后 ComponentDidMount 时,我应该将我的数据放在组件的 props 中,对吗?我没看到。

我看到 render 被调用了两次,并且在第一次初始化组件时,我无法访问 props 中的数据。在第二次渲染之前,似乎调度实际上并没有被调用。我基本上希望在最初设置组件时对调用调度的最佳方式有所了解。我实际上是在尝试执行以下操作,其中我使用容器组件从调度中获取数据,然后将其作为道具传递给子组件。但是我也想在ContainerComponent中初始化一些状态变量,然后将它们作为props传递给ChildComponent。问题是我要初始化的状态变量取决于从调度返回的数据,理想情况下我会在 ComponentWillMount 或 ComponentDidMount 中进行初始化。

import React from 'react';
import axios from 'axios';

import { connect } from 'react-redux';
import ChildComponent from './ChildComponent.js';

import { getTransactionsAll } from '../actions/actions.js';

class ContainerComponent extends React.Component {
  constructor() {
    super();
    this.state = {
      acctList:[],
      acctChecked:[],
      categoryList:[]
    }
}
  componentWillMount() {

    console.log("componentWillMount entered");

    this.props.get_data();
    console.log(this.props.searchProps.transactions_all);//this is undefined meaning the dispatch has not assigned the data yet...??

  }

  componentDidMount() {
    console.log("componentDidMount entered");
    console.log(this.props.searchProps.transactions_all);//this is undefined meaning the dispatch has not assigned the data yet...??
}

  render() {

    console.log("TransactionManagerContainer render entered");
    console.log(this.props.searchProps.transactions_all);//this is undefined the first time around meaning the dispatch has not assigned the data yet...??, but is defined on the second call to render after the dispatch has actually occurred...

return <ChildComponent
             data={this.props.searchProps.data}/>;
}

const mapStateToProps = (state) => ({
  searchProps: state.searchProps
});

export default connect(mapStateToProps, {getTransactionsAll})(TransactionManagerContainer);

这是我分配状态的减速器:

import { combineReducers } from 'redux'

import {GET_TRANSACTIONS } from '../actions/actions.js'
import {GET_TRANSACTIONS_ALL } from '../actions/actions.js'

const INITIAL_STATE = { defaultYear: 2016, transactions: []};

function get_transactions(state = INITIAL_STATE, action) {
  // console.log("this is in the reducer: get_transactions");
  // console.log(action);
  switch(action.type) {
    case GET_TRANSACTIONS:
      // return { ...state, transactions: action.payload };
      return Object.assign({}, state, {
        transactions: action.payload,
        selectedYear: action.selectedYear
      })
    default:
      return state;
  }
}

function get_transactions_all(state = INITIAL_STATE, action) {
  console.log("this is the value of action in the reducer: get_transactions_all");
  console.log(action);
  switch(action.type) {
    case GET_TRANSACTIONS_ALL:
      // return { ...state, transactions: action.payload };
      return Object.assign({}, state, {
        transactions_all: action.payload
      })
      console.log("this is the value of state in the reducer after being set");
      console.log(state);
    default:
      return state;
  }
}

const rootReducer = combineReducers({
  //stateProps: get_transactions,
  searchProps: get_transactions_all
})

export default rootReducer

这是我的行动:

import axios from 'axios';

export const GET_TRANSACTIONS = 'GET_TRANSACTIONS';

export function getTransactions(year) {
 return function(dispatch) {
  axios.get(`http://localhost:3001/api/transfilter?year=${year}&grouping=2`)
   .then(response => {
     dispatch({
       type: GET_TRANSACTIONS,
       payload: response.data,
       selectedYear: year
     });
   })
   .catch((error) => {
     console.log(error);
   })
 }
}

export const GET_TRANSACTIONS_ALL = 'GET_TRANSACTIONS_ALL';

export function getTransactionsAll(year) {
 return function(dispatch) {
  axios.get(`http://localhost:3001/api/trans?limit=20`)
   .then(response => {

     dispatch({
       type: GET_TRANSACTIONS_ALL,
       payload: response.data
     });
   })
   .catch((error) => {
     console.log(error);
   })
 }
}

【问题讨论】:

  • 在 react/nuclearjs 项目中,我们在 componentDidMount 中调用 fetch API 数据方法,这样应该可以工作。您能告诉我们您是如何在 HTML 模板中使用该组件的吗?
  • 抓取有效。一切正常。我只想知道最好的方法并更好地了解调度“生命周期”。就像实际何时调用 fetch 一样,因为它似乎不会立即调用,因为 render 被调用了两次,并且数据在第二次调用 render 之前不可用。
  • 你在哪里经过searchProps。您应该显示此容器父组件和您的 get_data 函数
  • 进行编辑以包含所有文件(操作、reducer 等)。我正在从我的 reducer 传递 searchProps。
  • 我想我误解了你的问题。您是在问为什么您的数据只存在于第二次渲染中?

标签: reactjs react-redux


【解决方案1】:

我相信您的主要问题是:

调用调度以获取 React 组件的初始数据的最佳方法是什么?

获取初始数据请求(或一般的任何 AJAX 请求)应在 componentDidMount 生命周期事件中进行。

这有几个原因,这里有两个重要的:

  1. Fiber 是 React 协调算法的下一个实现,它将能够根据需要启动和停止渲染以提高性能。这样做的一个权衡是 componentWillMount,另一个可能对发出 AJAX 请求有意义的生命周期事件,将是“不确定的”。这意味着 React 可能会在需要时在不同时间开始调用 componentWillMount。对于 AJAX 请求,这显然是一个糟糕的公式。

  2. 您不能保证 AJAX 请求在组件挂载之前不会被解析。如果是这样,那意味着您将尝试在未安装的组件上设置状态,这不仅不起作用,而且 React 会为您大喊大叫。在 componentDidMount 中做 AJAX 会保证有组件要更新。

致谢:从here了解到,还有here的讨论。

然后,你提出了很多小问题,我很难回答所有问题,但我会尽量涵盖大部分:

  • 看完以上内容,你现在应该明白为什么componentWillMountcomponentDidMount中的数据是undefined了。那只是因为数据还没有到;
  • 在第一次渲染组件时,您的数据为undefined 是正常的。初始渲染发生在数据到达之前;
  • 在第二次渲染期间定义数据是正常的。 dispatch 触发异步数据获取。在数据到来之后,reducer 被命中并且组件被重新渲染(这是第二次重新渲染)。
  • 如果您的主组件中的子组件需要数据 - 检查父 render 方法如果数据存在,则有条件地传递内部组件,仅当数据存在时。像这样:

    class ContainerComponent extends React.Component {
      // ... omitted for brevity
    
      render() {
        return (
          { this.props.searchProps.data ?
              <ChildComponent
                data={this.props.searchProps.data} />
              : <p>Loading</p>
          }
        );
      }
    }
    

【讨论】:

  • 感谢您的概念性解释。这说得通。所以我复制并粘贴了你上面的渲染,我得到一个错误:111 | return ( &gt; 112 | { this.props.searchProps.data? | ^ 113 | &lt;TransactionManagerView 114 | transactions={this.props.searchProps.data} /&gt; 115 | : &lt;p&gt;Loading&lt;/p&gt; 它的格式不干净,不知道如何在 cmets 中格式化。但基本上我得到了一个语法错误意外标记,其中句点是在第一次调用 this.props 时。
  • 不客气。嗯,我不确定——这个错误对我来说没有多大意义。也许这个问题是由this.props.searchProps 未定义引起的。将默认值设置为最初不存在的props 通常是一个好习惯。因为否则,在第一次渲染期间它们将始终为undefinedDeclare default props 看看问题是否解决。
  • 谢谢。我按照您在您回复的另一篇文章中的建议这样做了。在那里,设置默认道具也不起作用。我难住了。似乎我的默认道具被忽略了。我什至访问了我的 babel.rc 并确保我设置了 2016 初始化程序。我已经尝试了在构造函数中设置默认道具的静态方法以及使用 Component.defaultProps 在类外部进行设置。两者似乎都不起作用,因为我最初仍然不确定。
  • 我开始认为您的用例中有太多地方可能会导致麻烦。我的建议是拆分每个问题,尝试通过更简单的示例重现它,尝试创建一个 jsfiddle。否则 - 我或其他任何人都很难提供帮助 :) PS:See my edit in the other question you asked。您看到的这两个问题很有可能是相互关联的,我的回答可以帮助您解决这两个问题。
  • 我还有一个与这篇文章相关的问题,这是我最初的问题的一部分。根据 Redux 返回的数据来操作状态的正确方法或最佳方法是什么?如果我只能在第二次渲染中访问数据,那么我无法在渲染中的组件中分配状态,因为它会导致无限循环。例如,在从 Redux 返回的数据中,我想获取不同帐户类型的列表,并将其放入我的 ContainerComponent 本地的 acctList 状态对象中。我知道我可以在 Redux 中做到这一点,但我想将其隔离到 ContainerComponent。
猜你喜欢
  • 2019-08-24
  • 2021-10-14
  • 2018-05-05
  • 2019-02-15
  • 2018-06-28
  • 2019-04-06
  • 2019-10-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多