反应 Redux 调度
React Redux Dispatch
调用分派以获取 React 组件上的初始数据的最佳方法是什么。我的理解是 ComponentWillMount 在渲染之前被调用。所以理论上,如果我在 ComponentWillMount 上调用 dispatch,当我点击渲染然后点击 ComponentDidMount 时,我应该将我的数据放在组件的 props 中,对吧?我没看到。
我看到 render 被调用了两次,并且在第一次初始化组件时,我无法访问 props 中的数据。看起来 dispatch 直到第二次渲染才真正被调用。我基本上是想了解在最初设置组件时调用调度的最佳方式。我本质上是在尝试做类似下面的事情,我使用容器组件从调度中获取数据,然后将其作为道具传递给子组件。但我也想在 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);
})
}
}
我认为您的主要问题是:
What is the best way to call a dispatch to get initial data on a React component?
获取初始数据请求(或一般的任何 AJAX 请求)应该进入 componentDidMount
生命周期事件。
这有几个原因,这里有两个重要的原因:
Fiber 是 React 协调算法的下一个实现,将能够根据需要启动和停止渲染以获得性能优势。这样做的权衡之一是 componentWillMount 是另一个生命周期事件,在该事件中发出 AJAX 请求可能是有意义的,它将是“非确定性的”。这意味着 React 可能会在需要时在不同的时间开始调用 componentWillMount。对于 AJAX 请求,这显然是一个糟糕的公式。
您不能保证 AJAX 请求不会在组件安装前解决。如果是这样,那将意味着您将尝试在未安装的组件上设置状态,这不仅行不通,而且 React 会为此大喊大叫。在 componentDidMount 中执行 AJAX 将保证有一个组件要更新。
致谢:我是从 here, there is also a discussion 那里学到的。
然后,你提出了很多小问题,我很难回答所有问题,但我会尽量涵盖大部分问题:
- 看完上面的内容,你应该明白为什么你的数据在
componentWillMount
和componentDidMount
中是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>
}
);
}
}
调用分派以获取 React 组件上的初始数据的最佳方法是什么。我的理解是 ComponentWillMount 在渲染之前被调用。所以理论上,如果我在 ComponentWillMount 上调用 dispatch,当我点击渲染然后点击 ComponentDidMount 时,我应该将我的数据放在组件的 props 中,对吧?我没看到。
我看到 render 被调用了两次,并且在第一次初始化组件时,我无法访问 props 中的数据。看起来 dispatch 直到第二次渲染才真正被调用。我基本上是想了解在最初设置组件时调用调度的最佳方式。我本质上是在尝试做类似下面的事情,我使用容器组件从调度中获取数据,然后将其作为道具传递给子组件。但我也想在 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);
})
}
}
我认为您的主要问题是:
What is the best way to call a dispatch to get initial data on a React component?
获取初始数据请求(或一般的任何 AJAX 请求)应该进入 componentDidMount
生命周期事件。
这有几个原因,这里有两个重要的原因:
Fiber 是 React 协调算法的下一个实现,将能够根据需要启动和停止渲染以获得性能优势。这样做的权衡之一是 componentWillMount 是另一个生命周期事件,在该事件中发出 AJAX 请求可能是有意义的,它将是“非确定性的”。这意味着 React 可能会在需要时在不同的时间开始调用 componentWillMount。对于 AJAX 请求,这显然是一个糟糕的公式。
您不能保证 AJAX 请求不会在组件安装前解决。如果是这样,那将意味着您将尝试在未安装的组件上设置状态,这不仅行不通,而且 React 会为此大喊大叫。在 componentDidMount 中执行 AJAX 将保证有一个组件要更新。
致谢:我是从 here, there is also a discussion
然后,你提出了很多小问题,我很难回答所有问题,但我会尽量涵盖大部分问题:
- 看完上面的内容,你应该明白为什么你的数据在
componentWillMount
和componentDidMount
中是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> } ); } }