为什么我在应用程序加载时收到给定操作“Reducer [...] 在初始化期间返回未定义”?
Why do I get given action “Reducer […] returned undefined during initialization”,at the time app loads?
这可能是一个菜鸟问题。
我对 redux saga 的初体验。
商店:
import { createStore, applyMiddleware, compose } from 'redux';
import createSagaMiddleware from 'redux-saga';
import { createLogger } from 'redux-logger';
import rootReducer from '../reducers/RootReducer';
import rootSagas from '../sagas/RootSaga';
const enhancers = [];
const sagaMiddleware = createSagaMiddleware(),
logger = createLogger(),
initialState = {},
middleware = [ logger, sagaMiddleware ];
if ( process.env.NODE_ENV === 'development' ) {
const devToolsExtension = window.__REDUX_DEVTOOLS_EXTENSION__;
if ( typeof devToolsExtension === 'function' ) {
enhancers.push( devToolsExtension() );
}
}
const composedEnhancers = compose(
applyMiddleware( ...middleware ),
...enhancers
);
const store = createStore( rootReducer, initialState, composedEnhancers );
sagaMiddleware.run( rootSagas );
export default store;
操作:DashboardAction.js
import actionTypes from '../constants/ActionTypes';
export function getDataForDashboard( response ) {
console.log( 'in the action for dashboard' );
return {
type: actionTypes.GET_DATA_FOR_DASHBOARD,
payload: response
};
}
export function getDataForDashboardSuccess( response ) {
return {
type: actionTypes.GET_DASHBOARD_DATA_SUCCESS,
payload: response
};
}
export function getDataForDashboardError( error ) {
return {
type: actionTypes.GET_DASHBOARD_DATA_ERROR,
payload: error
};
}
常数:ActionTypes.js
export default {
GET_DATA_FOR_DASHBOARD: 'GET_DATA_FOR_DASHBOARD',
GET_DASHBOARD_DATA_SUCCESS: 'GET_DASHBOARD_DATA_SUCCESS',
GET_DASHBOARD_DATA_ERROR: 'GET_DASHBOARD_DATA_ERROR'
};
传奇:RootSaga.js
import { all } from 'redux-saga/effects';
import * as dashboardSagas from './DashboardSaga';
export default function* root() {
yield all( dashboardSagas.GetDataForDashboard() );
}
DashboardSaga.js
import { takeEvery } from 'redux-saga';
import { call, put } from 'redux-saga/effects';
import actionTypes from '../constants/ActionTypes';
import { getData } from '../services/DashboardService';
import {
getDataForDashboardSuccess,
getDataForDashboardError
} from '../actions/DashboardActions';
export function* GetDataForDashboard( action ) {
console.log( 'hello from inner method' );
try {
const response = yield call( getData, action );
//if ( response.id ) {
yield put( getDataForDashboardSuccess( response ) );
// }
} catch ( error ) {
yield put( getDataForDashboardError( error ) );
}
}
export function* watchGetDataForDashboard() {
console.log( 'hello from watcher' );
yield takeEvery( actionTypes.GET_DATA_FOR_DASHBOARD, GetDataForDashboard );
}
Reducer:DashboardReducer.js
/* eslint-disable no-unused-vars */
import actionTypes from '../constants/ActionTypes';
const initialState = {
error: false,
dashboard: {
orderdashboardmodel: {
rfs: '0',
pending: '0',
total: '0'
},
sitesdashboardmodel: {
up: '0',
down: '0',
total: '0',
notmonitored: '0'
},
support: {
open: '0',
late: '0',
escalated: '0'
}
}
};
function getDataForDashboard( state ) {
console.log( 'it comes here' );
return null;
}
function getDashboardSuccess( state, action ) {
console.log( 'IN SUCCESS' );
console.log( action.payload );
return {
...state,
dashboard: {
orderdashboardmodel: {
rfs: action.payload.dashboardModel.Order.RFS,
pending: action.payload.dashboardModel.Order.Pending,
total: action.payload.dashboardModel.Order.Total
},
sitesdashboardmodel: {
up: action.payload.dashboardModel.Sites.Up,
down: action.payload.dashboardModel.Sites.Down,
total: action.payload.dashboardModel.Sites.Total,
notmonitored: action.payload.dashboardModel.Sites.NotMonitored
},
support: {
open: action.payload.dashboardModel.Support.Open,
late: action.payload.dashboardModel.Support.Late,
escalated: action.payload.dashboardModel.Support.Escalated
}
}
};
}
function getDashboardError( state = initialState, action ) {
console.log( action.payload );
return {
...state,
error: true
};
}
export default function DashboardReducer( state = initialState, action ) {
console.log( 'IN REDUCER' );
console.log( action );
switch ( action.type ) {
case actionTypes.GET_DATA_FOR_DASHBOARD:
console.log( 'in the call for dashboard' );
getDataForDashboard( state );
console.log( 'in the call exit' );
break;
case actionTypes.GET_DASHBOARD_DATA_SUCCESS:
console.log( 'in the success call initator' );
getDashboardSuccess( state, action );
break;
case actionTypes.GET_DASHBOARD_DATA_ERROR:
getDashboardError( state, action );
break;
default:
return state;
}
}
发起调用的文件:Dashboard.js
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { Colors } from '../../common/themes';
import Globe from '../../common/assets/images/globe.png';
import DashboardCard from './DashboardCard';
import { DashboardData } from './DashboardData';
import { getDataForDashboard } from '../actions/DashboardActions';
class Dashboard extends React.Component {
componentDidMount() {
console.log( 'called before' );
this.props.getDataForDashboard( 'hello' );
console.log( 'called after' );
}
render() {
return (
<div>some html here</div>
);
}
}
const mapStateToProps = state => ( { dashboard: state.dashboard } );
const mapDispatchToProps = dispatch =>
bindActionCreators( { getDataForDashboard }, dispatch );
export default connect(
mapStateToProps,
mapDispatchToProps
)( Dashboard );
Package.json
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.0-14",
"@fortawesome/free-solid-svg-icons": "^5.1.0-11",
"@fortawesome/react-fontawesome": "0.1.0-11",
"aws-amplify": "^1.0.4",
"aws-amplify-react": "^1.0.4",
"axios": "^0.18.0",
"bootstrap": "^4.1.3",
"chroma-js": "^1.3.7",
"d3": "^5.5.0",
"i": "^0.3.6",
"npm": "^6.4.0",
"react": "^16.4.1",
"react-dom": "^16.4.1",
"react-loadable": "^5.4.0",
"react-motion": "^0.5.2",
"react-particles-js": "^2.3.0",
"react-redux": "^5.0.7",
"react-router-dom": "^4.3.1",
"react-simple-maps": "^0.12.1",
"reactstrap": "^6.3.1",
"redux": "^4.0.0",
"redux-logger": "^3.0.6",
"redux-saga": "^0.16.0"
},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.6",
"babel-loader": "^7.1.5",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.7.0",
"babel-preset-flow": "^6.23.0",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"babel-preset-stage-2": "^6.24.1",
"clean-webpack-plugin": "^0.1.19",
"css-loader": "^1.0.0",
"dotenv": "^6.0.0",
"eslint": "^5.2.0",
"eslint-plugin-import": "^2.13.0",
"eslint-plugin-react": "^7.10.0",
"file-loader": "^1.1.11",
"html-webpack-plugin": "^3.2.0",
"jest": "^23.4.2",
"open-browser-webpack-plugin": "0.0.5",
"prettier": "^1.14.0",
"prettier-eslint": "^8.8.2",
"radium": "^0.24.1",
"style-loader": "^0.21.0",
"webpack": "^4.12.2",
"webpack-cli": "^3.0.8",
"webpack-dev-server": "^3.1.4"
},
"jest": {
"verbose": true,
"testURL": "http://localhost/"
}
问题
Saga 永远不会被调用。
以下错误:
Uncaught Error: Given action "GET_DATA_FOR_DASHBOARD", reducer "dashboardReducer" returned undefined. To ignore an action, you must explicitly return the previous state. If you want this reducer to hold no value, you can return null instead of undefined
我尝试了 reducer 中的所有组合,但这个错误永远不会出现。也许有一双新鲜的眼睛可以挑出我所缺少的。
根据docs
The reducer is a pure function that takes the previous state and an action, and returns the next state.
然后您可以看到这是因为您正在中断 switch 语句而不是返回 DashboardReducer.js
中的新状态。它应该看起来像这样:
export default function DashboardReducer( state = initialState, action ) {
switch ( action.type ) {
case actionTypes.GET_DATA_FOR_DASHBOARD:
// note: the function you call returns null. That might not be
// what you are trying to do so just be aware of that.
return getDataForDashboard( state );
case actionTypes.GET_DASHBOARD_DATA_SUCCESS:
return getDashboardSuccess( state, action );
case actionTypes.GET_DASHBOARD_DATA_ERROR:
return getDashboardError( state, action );
default:
return state;
}
}
这可能是一个菜鸟问题。
我对 redux saga 的初体验。
商店:
import { createStore, applyMiddleware, compose } from 'redux';
import createSagaMiddleware from 'redux-saga';
import { createLogger } from 'redux-logger';
import rootReducer from '../reducers/RootReducer';
import rootSagas from '../sagas/RootSaga';
const enhancers = [];
const sagaMiddleware = createSagaMiddleware(),
logger = createLogger(),
initialState = {},
middleware = [ logger, sagaMiddleware ];
if ( process.env.NODE_ENV === 'development' ) {
const devToolsExtension = window.__REDUX_DEVTOOLS_EXTENSION__;
if ( typeof devToolsExtension === 'function' ) {
enhancers.push( devToolsExtension() );
}
}
const composedEnhancers = compose(
applyMiddleware( ...middleware ),
...enhancers
);
const store = createStore( rootReducer, initialState, composedEnhancers );
sagaMiddleware.run( rootSagas );
export default store;
操作:DashboardAction.js
import actionTypes from '../constants/ActionTypes';
export function getDataForDashboard( response ) {
console.log( 'in the action for dashboard' );
return {
type: actionTypes.GET_DATA_FOR_DASHBOARD,
payload: response
};
}
export function getDataForDashboardSuccess( response ) {
return {
type: actionTypes.GET_DASHBOARD_DATA_SUCCESS,
payload: response
};
}
export function getDataForDashboardError( error ) {
return {
type: actionTypes.GET_DASHBOARD_DATA_ERROR,
payload: error
};
}
常数:ActionTypes.js
export default {
GET_DATA_FOR_DASHBOARD: 'GET_DATA_FOR_DASHBOARD',
GET_DASHBOARD_DATA_SUCCESS: 'GET_DASHBOARD_DATA_SUCCESS',
GET_DASHBOARD_DATA_ERROR: 'GET_DASHBOARD_DATA_ERROR'
};
传奇:RootSaga.js
import { all } from 'redux-saga/effects';
import * as dashboardSagas from './DashboardSaga';
export default function* root() {
yield all( dashboardSagas.GetDataForDashboard() );
}
DashboardSaga.js
import { takeEvery } from 'redux-saga';
import { call, put } from 'redux-saga/effects';
import actionTypes from '../constants/ActionTypes';
import { getData } from '../services/DashboardService';
import {
getDataForDashboardSuccess,
getDataForDashboardError
} from '../actions/DashboardActions';
export function* GetDataForDashboard( action ) {
console.log( 'hello from inner method' );
try {
const response = yield call( getData, action );
//if ( response.id ) {
yield put( getDataForDashboardSuccess( response ) );
// }
} catch ( error ) {
yield put( getDataForDashboardError( error ) );
}
}
export function* watchGetDataForDashboard() {
console.log( 'hello from watcher' );
yield takeEvery( actionTypes.GET_DATA_FOR_DASHBOARD, GetDataForDashboard );
}
Reducer:DashboardReducer.js /* eslint-disable no-unused-vars */
import actionTypes from '../constants/ActionTypes';
const initialState = {
error: false,
dashboard: {
orderdashboardmodel: {
rfs: '0',
pending: '0',
total: '0'
},
sitesdashboardmodel: {
up: '0',
down: '0',
total: '0',
notmonitored: '0'
},
support: {
open: '0',
late: '0',
escalated: '0'
}
}
};
function getDataForDashboard( state ) {
console.log( 'it comes here' );
return null;
}
function getDashboardSuccess( state, action ) {
console.log( 'IN SUCCESS' );
console.log( action.payload );
return {
...state,
dashboard: {
orderdashboardmodel: {
rfs: action.payload.dashboardModel.Order.RFS,
pending: action.payload.dashboardModel.Order.Pending,
total: action.payload.dashboardModel.Order.Total
},
sitesdashboardmodel: {
up: action.payload.dashboardModel.Sites.Up,
down: action.payload.dashboardModel.Sites.Down,
total: action.payload.dashboardModel.Sites.Total,
notmonitored: action.payload.dashboardModel.Sites.NotMonitored
},
support: {
open: action.payload.dashboardModel.Support.Open,
late: action.payload.dashboardModel.Support.Late,
escalated: action.payload.dashboardModel.Support.Escalated
}
}
};
}
function getDashboardError( state = initialState, action ) {
console.log( action.payload );
return {
...state,
error: true
};
}
export default function DashboardReducer( state = initialState, action ) {
console.log( 'IN REDUCER' );
console.log( action );
switch ( action.type ) {
case actionTypes.GET_DATA_FOR_DASHBOARD:
console.log( 'in the call for dashboard' );
getDataForDashboard( state );
console.log( 'in the call exit' );
break;
case actionTypes.GET_DASHBOARD_DATA_SUCCESS:
console.log( 'in the success call initator' );
getDashboardSuccess( state, action );
break;
case actionTypes.GET_DASHBOARD_DATA_ERROR:
getDashboardError( state, action );
break;
default:
return state;
}
}
发起调用的文件:Dashboard.js
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { Colors } from '../../common/themes';
import Globe from '../../common/assets/images/globe.png';
import DashboardCard from './DashboardCard';
import { DashboardData } from './DashboardData';
import { getDataForDashboard } from '../actions/DashboardActions';
class Dashboard extends React.Component {
componentDidMount() {
console.log( 'called before' );
this.props.getDataForDashboard( 'hello' );
console.log( 'called after' );
}
render() {
return (
<div>some html here</div>
);
}
}
const mapStateToProps = state => ( { dashboard: state.dashboard } );
const mapDispatchToProps = dispatch =>
bindActionCreators( { getDataForDashboard }, dispatch );
export default connect(
mapStateToProps,
mapDispatchToProps
)( Dashboard );
Package.json
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.0-14",
"@fortawesome/free-solid-svg-icons": "^5.1.0-11",
"@fortawesome/react-fontawesome": "0.1.0-11",
"aws-amplify": "^1.0.4",
"aws-amplify-react": "^1.0.4",
"axios": "^0.18.0",
"bootstrap": "^4.1.3",
"chroma-js": "^1.3.7",
"d3": "^5.5.0",
"i": "^0.3.6",
"npm": "^6.4.0",
"react": "^16.4.1",
"react-dom": "^16.4.1",
"react-loadable": "^5.4.0",
"react-motion": "^0.5.2",
"react-particles-js": "^2.3.0",
"react-redux": "^5.0.7",
"react-router-dom": "^4.3.1",
"react-simple-maps": "^0.12.1",
"reactstrap": "^6.3.1",
"redux": "^4.0.0",
"redux-logger": "^3.0.6",
"redux-saga": "^0.16.0"
},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.6",
"babel-loader": "^7.1.5",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.7.0",
"babel-preset-flow": "^6.23.0",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"babel-preset-stage-2": "^6.24.1",
"clean-webpack-plugin": "^0.1.19",
"css-loader": "^1.0.0",
"dotenv": "^6.0.0",
"eslint": "^5.2.0",
"eslint-plugin-import": "^2.13.0",
"eslint-plugin-react": "^7.10.0",
"file-loader": "^1.1.11",
"html-webpack-plugin": "^3.2.0",
"jest": "^23.4.2",
"open-browser-webpack-plugin": "0.0.5",
"prettier": "^1.14.0",
"prettier-eslint": "^8.8.2",
"radium": "^0.24.1",
"style-loader": "^0.21.0",
"webpack": "^4.12.2",
"webpack-cli": "^3.0.8",
"webpack-dev-server": "^3.1.4"
},
"jest": {
"verbose": true,
"testURL": "http://localhost/"
}
问题 Saga 永远不会被调用。 以下错误:
Uncaught Error: Given action "GET_DATA_FOR_DASHBOARD", reducer "dashboardReducer" returned undefined. To ignore an action, you must explicitly return the previous state. If you want this reducer to hold no value, you can return null instead of undefined
我尝试了 reducer 中的所有组合,但这个错误永远不会出现。也许有一双新鲜的眼睛可以挑出我所缺少的。
根据docs
The reducer is a pure function that takes the previous state and an action, and returns the next state.
然后您可以看到这是因为您正在中断 switch 语句而不是返回 DashboardReducer.js
中的新状态。它应该看起来像这样:
export default function DashboardReducer( state = initialState, action ) {
switch ( action.type ) {
case actionTypes.GET_DATA_FOR_DASHBOARD:
// note: the function you call returns null. That might not be
// what you are trying to do so just be aware of that.
return getDataForDashboard( state );
case actionTypes.GET_DASHBOARD_DATA_SUCCESS:
return getDashboardSuccess( state, action );
case actionTypes.GET_DASHBOARD_DATA_ERROR:
return getDashboardError( state, action );
default:
return state;
}
}