与 redux 反应并且 react-router 分派动作两次
react with redux and react-router dispatches action twice
我刚开始尝试使用 React 和 Redux,在途中我遇到了几个问题。
当我尝试在路由更改时呈现异步数据时,调度的操作被触发了两次。首先是未定义的,然后是真实数据。
这是我的商店
import { createStore, combineReducers, applyMiddleware } from 'redux'
import createLogger from 'redux-logger'
import thunk from 'redux-thunk'
import { routerReducer, routerMiddleware, push } from 'react-router-redux'
import reducers from '../reducers'
import { browserHistory } from 'react-router';
const middleware = [ thunk ];
if (process.env.NODE_ENV !== 'production') {
middleware.push(createLogger());
}
middleware.push(routerMiddleware(browserHistory));
// Add the reducer to your store on the `routing` key
const store = createStore(
combineReducers({
reducers,
routing: routerReducer
}),
applyMiddleware(...middleware),
)
export default store;
减速器
export const RESOLVED_GET_PROFILE = 'RESOLVED_GET_PROFILE'
const profileReducer = (state = {}, action) => {
switch (action.type) {
case 'SET_PROFILE':
return {profile: action.profile}
default:
return state;
}
};
export default profileReducer;
操作
import * as types from './actionTypes';
import Api from '../middleware/Api';
export function getProfile() {
return dispatch => {
dispatch(setLoadingProfileState()); // Show a loading spinner
Api.get('profile').then(profile => {
dispatch(doneFetchingProfile);
dispatch(setProfile(profile));
}).catch(error => {
dispatch(showError(error));
throw(error);
});
}
}
function setProfile(data) {
return {
type: types.SET_PROFILE,
profile: data
}
}
function setLoadingProfileState() {
return {
type: types.SHOW_SPINNER,
loaded: false
}
}
function doneFetchingProfile() {
return {
type: types.HIDE_SPINNER,
loaded: true
}
}
function showError() {
return {
type: types.SHOW_ERROR,
loaded: false,
error: 'error'
}
}
这是我的组件
import React, {PropTypes, Component} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import * as profileActions from '../../../actions/profileActions';
class Profile extends Component {
static propTypes = {
profile: PropTypes.object.isRequired,
};
constructor(props) {
super(props);
this.state = {
profile:{
username: '',
password: '',
email: ''
}
}
this.onUpdate = this.onUpdate.bind(this)
}
onUpdate(event) {
alert()
}
componentDidMount() {
//here I dispatch the action
this.props.actions.getProfile()
}
componentWillReceiveProps(nextProps) {
}
render() {
console.log(this.props)
//this.props.profile on first is undefined and then filled
const { profile } = this.props.profile
return (
<div>
</div>
);
}
}
function mapStateToProps(state) {
return {
profile: state.default.profile,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(profileActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Profile);
我哪里错了?
您调度了 2 个操作
dispatch(doneFetchingProfile);
dispatch(setProfile(profile));
它们中的第一个没有数据,看起来 tihs 操作设置为声明一些数据并更新您的组件。
你说//this.props.profile on first is undefined and then filled
那是因为在第一次渲染中,state.profile
是undefined
,直到请求响应到达并调度setProfile
动作。
Andrew 注意到您正在呼叫 dispatch(doneFetchingProfile)
也存在问题。由于您使用的是 redux-thunk,这将触发调用 doneFetchingProfile(dispatch, getState)
,但操作 HIDE_SPINNER
将永远不会被调度。
更新:您的代码没有任何问题。您可以在 SHOW_SPINNER
之前看到 console.log(this.props)
的输出并且没有 profile
因为状态中也没有 profile
。
然后当您的请求成功时,profile
被设置为状态,然后传递给您的组件,然后您可以在日志中看到 profile
已设置。这些不是派发的动作,这是你的道具的日志。
第一次是未定义的,因为reducer中声明的初始状态是{}
(这里也没有profile
)
如果你改变
const profileReducer = (state = {}, action) => {
到
const profileReducer = (state = {profile: 'some initial value'}, action) => {
你会看到第一个console.log(this.props)
会显示profile
,值为'some initial value'
,然后变为远程数据。
这就是这里发生的事情
- 您的组件在控制台上呈现并显示 undefined,因为目前还没有配置文件数据。
组件安装后,它调用 componentDidmount,它会触发一个操作以从 url.
获取数据
您从 api 获取数据并更新 redux 状态,这也会更新您的组件。
因此再次调用渲染函数,这次它显示配置文件数据。
两次都没有调度。代码非常好。
我刚开始尝试使用 React 和 Redux,在途中我遇到了几个问题。
当我尝试在路由更改时呈现异步数据时,调度的操作被触发了两次。首先是未定义的,然后是真实数据。
这是我的商店
import { createStore, combineReducers, applyMiddleware } from 'redux'
import createLogger from 'redux-logger'
import thunk from 'redux-thunk'
import { routerReducer, routerMiddleware, push } from 'react-router-redux'
import reducers from '../reducers'
import { browserHistory } from 'react-router';
const middleware = [ thunk ];
if (process.env.NODE_ENV !== 'production') {
middleware.push(createLogger());
}
middleware.push(routerMiddleware(browserHistory));
// Add the reducer to your store on the `routing` key
const store = createStore(
combineReducers({
reducers,
routing: routerReducer
}),
applyMiddleware(...middleware),
)
export default store;
减速器
export const RESOLVED_GET_PROFILE = 'RESOLVED_GET_PROFILE'
const profileReducer = (state = {}, action) => {
switch (action.type) {
case 'SET_PROFILE':
return {profile: action.profile}
default:
return state;
}
};
export default profileReducer;
操作
import * as types from './actionTypes';
import Api from '../middleware/Api';
export function getProfile() {
return dispatch => {
dispatch(setLoadingProfileState()); // Show a loading spinner
Api.get('profile').then(profile => {
dispatch(doneFetchingProfile);
dispatch(setProfile(profile));
}).catch(error => {
dispatch(showError(error));
throw(error);
});
}
}
function setProfile(data) {
return {
type: types.SET_PROFILE,
profile: data
}
}
function setLoadingProfileState() {
return {
type: types.SHOW_SPINNER,
loaded: false
}
}
function doneFetchingProfile() {
return {
type: types.HIDE_SPINNER,
loaded: true
}
}
function showError() {
return {
type: types.SHOW_ERROR,
loaded: false,
error: 'error'
}
}
这是我的组件
import React, {PropTypes, Component} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import * as profileActions from '../../../actions/profileActions';
class Profile extends Component {
static propTypes = {
profile: PropTypes.object.isRequired,
};
constructor(props) {
super(props);
this.state = {
profile:{
username: '',
password: '',
email: ''
}
}
this.onUpdate = this.onUpdate.bind(this)
}
onUpdate(event) {
alert()
}
componentDidMount() {
//here I dispatch the action
this.props.actions.getProfile()
}
componentWillReceiveProps(nextProps) {
}
render() {
console.log(this.props)
//this.props.profile on first is undefined and then filled
const { profile } = this.props.profile
return (
<div>
</div>
);
}
}
function mapStateToProps(state) {
return {
profile: state.default.profile,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(profileActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Profile);
我哪里错了?
您调度了 2 个操作
dispatch(doneFetchingProfile);
dispatch(setProfile(profile));
它们中的第一个没有数据,看起来 tihs 操作设置为声明一些数据并更新您的组件。
你说//this.props.profile on first is undefined and then filled
那是因为在第一次渲染中,state.profile
是undefined
,直到请求响应到达并调度setProfile
动作。
Andrew 注意到您正在呼叫 dispatch(doneFetchingProfile)
也存在问题。由于您使用的是 redux-thunk,这将触发调用 doneFetchingProfile(dispatch, getState)
,但操作 HIDE_SPINNER
将永远不会被调度。
更新:您的代码没有任何问题。您可以在 SHOW_SPINNER
之前看到 console.log(this.props)
的输出并且没有 profile
因为状态中也没有 profile
。
然后当您的请求成功时,profile
被设置为状态,然后传递给您的组件,然后您可以在日志中看到 profile
已设置。这些不是派发的动作,这是你的道具的日志。
第一次是未定义的,因为reducer中声明的初始状态是{}
(这里也没有profile
)
如果你改变
const profileReducer = (state = {}, action) => {
到
const profileReducer = (state = {profile: 'some initial value'}, action) => {
你会看到第一个console.log(this.props)
会显示profile
,值为'some initial value'
,然后变为远程数据。
这就是这里发生的事情
- 您的组件在控制台上呈现并显示 undefined,因为目前还没有配置文件数据。
组件安装后,它调用 componentDidmount,它会触发一个操作以从 url.
获取数据
您从 api 获取数据并更新 redux 状态,这也会更新您的组件。
因此再次调用渲染函数,这次它显示配置文件数据。
两次都没有调度。代码非常好。