以下所有代码在重构开始时都在1个文件中并且运行良好。我简化了一些代码。
我的reducers
文件夹:
index.js :
import { combineReducers } from 'redux'
import address from './address'
import questions from './questions'
export default combineReducers({
address,
questions
});
initialState.js :
import { uniqueID } from '../utils/index';
const defaultQuestion = {
title: 'What is the address of the property?',
id: 0,
question_type: 'address'
};
export const initialState = {
questions: [defaultQuestion],
sessionID: uniqueID(),
session: {},
currentQuestion: defaultQuestion,
currentAnswer: '',
addressSelectd: false,
amount: 0,
address: {
address: {},
isPendind: false,
isRejected: false,
isFulfilled: false,
message: '',
}
};
address.js :
import {
ON_SELECT_ADDRESS,
SAVE_ADDRESS_PENDING,
SAVE_ADDRESS_FULFILLED,
SAVE_ADDRESS_REJECTED,
} from '../constants/Constants';
import { initialState } from './initialState'
import { nextQuestion } from './questions'
export default function reduce(state = initialState, action) {
switch (action.type) {
case ON_SELECT_ADDRESS:
return {...state,
currentAnswer: action.payload,
addressSelectd: true
};
case SAVE_ADDRESS_PENDING:
return {...state,
address: {
isPendind: true,
},
};
case SAVE_ADDRESS_FULFILLED:
return {...state,
address: {
isPendind: false,
isRejected: false,
isFulfilled: true,
address: action.payload.address,
},
amount: action.payload.amount,
currentAnswer: '',
currentQuestion: nextQuestion(state),
};
case SAVE_ADDRESS_REJECTED:
// if (action.payload == 'incorrect_address')
return {...state,
currentAnswer: '',
address: {
address: {},
isPendind: false,
isFulfilled: false,
isRejected: true,
message: 'Please find valid address',
},
};
default:
return state;
}
}
questions.js :
import {
ON_CHANGE_ANSWER,
ON_CHANGE_QUESTION,
GET_QUESTIONS,
CREATE_SESSION,
SAVE_ANSWER,
SAVE_CURRENT_ANSWER,
ON_FINISH,
} from '../constants/Constants';
import { initialState } from './initialState'
import { isNullOrUndefined } from 'util';
export const nextQuestion = (state) => {
let nextId = state.currentQuestion.direct_question_id;
if (isNullOrUndefined(nextId)) {
if (state.currentAnswer === 'yes') {
nextId = state.currentQuestion.yes_question_id;
} else if (state.currentAnswer === 'no') {
nextId = state.currentQuestion.no_question_id;
}
}
return state.questions.find((q) => {
return q.id === nextId;
});
}
export default function reduce(state = initialState, action) {
switch (action.type) {
case ON_CHANGE_ANSWER:
return {...state,
currentAnswer: action.payload
};
case ON_CHANGE_QUESTION:
return {...state,
currentQuestion: action.payload
};
case GET_QUESTIONS:
return {...state,
questions: action.payload,
currentQuestion: action.payload[0]
};
case CREATE_SESSION:
return {...state,
session: action.payload,
};
case SAVE_CURRENT_ANSWER:
return {...state,
currentAnswer: action.payload,
};
case SAVE_ANSWER:
return {...state,
currentAnswer: '',
currentQuestion: nextQuestion(state),
};
case ON_FINISH:
return initialState;
default:
return state;
}
}
我在Chrome控制台中有很多错误,例如:
Warning: Failed prop type: Invalid prop `questions` of type `object` supplied to `MyApp`, expected `array`.
Warning: Failed prop type: The prop `currentAnswer` is marked as required in `MyApp`, but its value is `undefined`.
但仅适用于questions
减速机。如果我在initialState文件中添加console.log
,我只看了一次(我想应该显示2次)
似乎问题reducer还没有被添加到root reducer。
configureStore:
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import reducers from '../reducers/index';
import { createLogger } from 'redux-logger';
import DevTools from '../web/containers/DevTools';
const createDevStoreWithMiddleware = compose(
applyMiddleware(thunk),
applyMiddleware(createLogger()),
DevTools.instrument()
)(createStore);
export default function configureStore() {
const store = createDevStoreWithMiddleware(reducers);
return store;
}
更新
App.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import AddressSearch from '../components/AddressSearch';
import FinalScreen from '../components/FinalScreen';
import {
onChangeAnswer,
onChangeQuestion,
getQuestions,
saveAnswer,
createSession,
onFinish
} from '../../actions/actions';
class MyApp extends Component {
static propTypes = {
questions: PropTypes.array.isRequired,
sessionID: PropTypes.string.isRequired,
session: PropTypes.object.isRequired,
currentQuestion: PropTypes.object.isRequired,
currentAnswer: PropTypes.string.isRequired,
address: PropTypes.object.isRequired,
amount: PropTypes.number,
};
componentDidMount() {
this.props.actions.getQuestions();
this.props.actions.createSession();
}
onReset() {
this.props.actions.onFinish();
this.componentDidMount();
}
nextQuestion(text) {
if (text.length > 0) {
this.props.actions.saveAnswer(text);
}
}
renderAnswers() {
const props = this.props;
if (props.currentQuestion.question_type === 'address') {
return <AddressSearch
currentAnswer={props.currentAnswer}
message={props.address.message}
/>;
} else if (props.currentQuestion.question_type === 'text') {
return [
<input
className="question-input"
value={props.currentAnswer}
onChange={(event) => props.actions.onChangeAnswer(event.target.value)}
/>,
<button
className="main-button"
onClick={() => this.nextQuestion(props.currentAnswer)}>
NEXT
</button>
];
} else if (props.currentQuestion.question_type === 'bool') {
return [
<button
className="yes-no-button"
onClick={() => this.nextQuestion('yes')}>
YES
</button>,
<button
className="yes-no-button"
onClick={() => this.nextQuestion('no')}>
NO
</button>
];
} else if (props.currentQuestion.question_type === 'screen') {
return (
<button
className="main-button"
onClick={() => this.onReset()}>
Back
</button>
);
}
}
containerInner() {
if (this.props.currentQuestion.question_type === 'success') {
return <FinalScreen amount={this.props.amount} />;
} else {
return [
<div key={0} className="question">
{this.props.currentQuestion.title}
</div>,
<div key={1} className="answer">
{this.renderAnswers()}
</div>
];
}
}
render() {
return (
<div className="react-native-web">
{this.containerInner()}
</div>
);
}
}
const mapStateToProps = (state) => {
return state;
};
const mapDispatchToProps = (dispatch) => {
return {
actions: {
getQuestions: () => dispatch(getQuestions()),
createSession: () => dispatch(createSession()),
saveAnswer: (text) => dispatch(saveAnswer(text)),
onChangeAnswer: (text) => dispatch(onChangeAnswer(text)),
onChangeQuestion: (obj) => dispatch(onChangeQuestion(obj)),
onFinish: () => dispatch(onFinish()),
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(MyApp);
答案 0 :(得分:1)
在mapStateToProps
,
const mapStateToProps = (state) => {
return state;
};
state
应根据其reducers
进行拆分。这是因为combineReducers
。
所以,当你想在使用combineReducers
时获得实际状态时,你必须做这样的事情 -
const mapStateToProps = (state) => {
return state.address; // or state.question
};
如果你想发送状态的所有数据(即属于两个reducer),你可以这样做 -
const mapStateToProps = (state) => {
return Object.assign({}, state.address, state.question);
};
或者您必须在reducer
代码中处理它。
注意:我没有尝试过,在执行此操作时必须小心,因为由于创建了单独的对象,因此可能会导致更新问题。
编辑:有些人考虑过实施。
PS:我认为reducer
设计不正确。我的意思是address
以及questions
减速器都具有相同的初始状态。所以,当你执行combineReducer()
时,store.getState()
(即商店状态)会变成这样的 -
state = {
address: {
questions: [{
title: 'What is the address of the property?',
id: 0,
question_type: 'address'
}],
sessionID: 1234,
session: {},
currentQuestion: defaultQuestion,
currentAnswer: '',
addressSelectd: false,
amount: 0,
address: {
address: {},
isPendind: false,
isRejected: false,
isFulfilled: false,
message: '',
}
},
questions: {
questions: [{
title: 'What is the address of the property?',
id: 0,
question_type: 'address'
}],
sessionID: 1234,
session: {},
currentQuestion: defaultQuestion,
currentAnswer: '',
addressSelectd: false,
amount: 0,
address: {
address: {},
isPendind: false,
isRejected: false,
isFulfilled: false,
message: '',
}
}
};
而不是这个 -
state = {
questions: [{
title: 'What is the address of the property?',
id: 0,
question_type: 'address'
}],
sessionID: 1234,
session: {},
currentQuestion: defaultQuestion,
currentAnswer: '',
addressSelectd: false,
amount: 0,
address: {
address: {},
isPendind: false,
isRejected: false,
isFulfilled: false,
message: '',
}
}
我强烈建议您将常见状态(如currentAnswer
和currentQuestion
)移动到单独的缩减器中。
编辑2 :我刚刚使用以下代码验证了Object.assign()
不正确的事情。
var address = {
questions: [{
title: 'What is the address of the property?',
id: 0,
question_type: 'address'
}],
sessionID: 12345,
session: {},
currentQuestion: defaultQuestion,
currentAnswer: '',
addressSelectd: false,
amount: 0,
address: {
address: {},
isPendind: false,
isRejected: false,
isFulfilled: false,
message: ''
}
};
var questions = {
questions: [{
title: 'What is the address of the property?',
id: 0,
question_type: 'address'
}],
sessionID: 1234,
session: {},
currentQuestion: defaultQuestion,
currentAnswer: '',
addressSelectd: false,
amount: 0,
address: {
address: {},
isPendind: false,
isRejected: false,
isFulfilled: false,
message: ''
}
};
var result = Object.assign({}, address, questions);
console.log(result);
输出是 -
{
"questions": [
{
"title": "What is the address of the property?",
"id": 0,
"question_type": "address"
}
],
"sessionID": 1234,
"session": {},
"currentQuestion": {
"title": "What is the address of the property?",
"id": 0,
"question_type": "address"
},
"currentAnswer": "",
"addressSelectd": false,
"amount": 0,
"address": {
"address": {},
"isPendind": false,
"isRejected": false,
"isFulfilled": false,
"message": ""
}
}
此处,address
有sessionID: 12345
,而questions
有sessionID: 1234
,但result
有sessionID: 1234
。
因此,Object.assign()
会将address
设置的值替换为question
的值。这就是为什么它似乎有效。
正确的方法是重新设计减速器,使其在新的减速器中具有共同状态。