Redux reducer 向数组添加新对象
Redux reducer add new object to array
我在提交后通过调度操作将新对象添加到对象数组时遇到问题。这样做的目的是跨站点显示 flash 消息。我有两个组件负责显示消息,FlashMessagesList 和 FlashMessage。服务器还响应消息对象,需要将其添加到消息数组中。问题是当动作被分派时,我可以看到它是在 redux devtools 中分派的,但是状态从来没有改变过我所做的任何事情,它仍然是空数组,我很坚持这一点,所以欢迎任何建议。这是相关代码:
/* flashMessagesList 组件 */
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import FlashMessage from './FlashMessage';
import { deleteFlashMessage } from '../../actions/actionFlashMessage';
class FlashMessagesList extends Component { //this is conected component cause we need data from store
render() {
const messages = this.props.messages.map(message =>
<FlashMessage key={message.id} message={message} deleteFlashMessage={this.props.deleteFlashMessage}/>
);
return (
<div>{messages}</div>
);
}
}
function mapStateToProps(state) {
return { //here we take a slice of global state
messages: state.flash.messages //define this in root reducer
};
}
function mapDispatchToProps(dispatch) {
return {
deleteFlashMessage: () => dispatch(deleteFlashMessage())
};
}
FlashMessagesList.propTypes = { //what this component will take
messages: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
style: PropTypes.string.isRequired,
text: PropTypes.string.isRequired
})
),
deleteFlashMessage: PropTypes.func.isRequired
};
export default connect(mapStateToProps, mapDispatchToProps)(FlashMessagesList); //pass message from store to this component
/* flashMessage 组件 */
import React, { Component, PropTypes } from 'react';
import classnames from 'classnames';
class FlashMessage extends Component {
deleteFlashMessage = () =>{
this.props.deleteFlashMessage(this.props.message.id); //dispatch some action that it has on it props
}
render() {
const { id, style, text } = this.props.message; //we can deconstruct message cause it is object
return (
<div className={classnames('alert', {
'alert-success': style === 'success',
'alert-danger': style === 'danger'
})}
>
{ text }
<button className="close" onClick={this.deleteFlashMessage}><span>×</span></button>
</div>
);
}
}
FlashMessage.propTypes = {
message: PropTypes.object.isRequired,
deleteFlashMessage: PropTypes.func.isRequired
};
export default FlashMessage;
/* reducerFlashMessage */
import * as types from '../actions/actionTypes';
import deepFreeze from 'deep-freeze';
import expect from 'expect';
const INITIAL_STATE = {
messages: []
};
const reducerFlashMessage = (state = INITIAL_STATE, action) => {
switch (action.types) {
case types.ADD_FLASH_MESSAGE:
return { ...state,
messages: [ ...state.messages, {
id: action.message.id,
style: action.message.style,
text: action.message.text
}]
};
}
return state;
};
export default reducerFlashMessage;
/* actionflashMessage */
import * as types from './actionTypes';
export function addFlashMessage(message) {
return {
type: types.ADD_FLASH_MESSAGE,
message
};
}
/* registerUser-我发送 addFlashMessage 的地方 */
export function registerUser({
timezone,
name,
email,
password,
passwordConfirmation
}) {
return dispatch => {
dispatch(isLoading(true));
axios.post('/auth/signup', {
timezone,
name,
email,
password,
passwordConfirmation
})
.then(response => {
dispatch(_registerUserSuccess(response.data.errors, response.data.message));
dispatch(isLoading(false));
dispatch(addFlashMessage(response.data.message));
dispatch(formReset());
// browserHistory.push('/signin');
setTimeout(() => {
// dispatch(_hideNotifications());
}, 2000);
// document.getElementById('form').reset();
})
.catch(error => {
if (error.response) {
dispatch(registerUserFailure(error.response.data.errors, error.response.data.message));
dispatch(isLoading(false));
dispatch(addFlashMessage(error.response.data.message));
}
else {
// Something happened in setting up the request that triggered an Error
console.log(error.message);
}
});
};
}
/* validation.js 来自我创建消息对象的服务器*/
export function validateSignupForm(payload) {
const errors = {};
const message = {};
if(validator.isEmpty(payload.name)) {
errors.name = 'Name is required';
}
if(validator.isEmpty(payload.email)) {
errors.email = 'Email is required';
}
if(validator.isEmpty(payload.password)) {
errors.password = 'Password is required';
}
if(validator.isEmpty(payload.passwordConfirmation)) {
errors.passwordConfirmation = 'Password confirmation is required';
}
if(validator.isEmpty(payload.timezone)) {
errors.timezone = 'Timezone is required';
}
if (payload.email && !validator.isEmail(payload.email)) {
errors.email = 'Please provide a correct email address.';
}
if (payload.password && payload.password.length < 4) {
errors.password = 'Password must have at least 4 characters.';
}
if (payload.passwordConfirmation && !validator.equals(payload.password, payload.passwordConfirmation)) {
errors.password = 'Passwords must match.';
}
if (!isEmpty(errors)) {
message.id = shortid.generate(),
message.style = 'danger';
message.text = 'Check the form for errors.';
}
return {
success: isEmpty(errors),
message,
errors
};
}
你的减速器有错字。而不是
switch (action.types) {
应该阅读
switch (action.type) {
我在提交后通过调度操作将新对象添加到对象数组时遇到问题。这样做的目的是跨站点显示 flash 消息。我有两个组件负责显示消息,FlashMessagesList 和 FlashMessage。服务器还响应消息对象,需要将其添加到消息数组中。问题是当动作被分派时,我可以看到它是在 redux devtools 中分派的,但是状态从来没有改变过我所做的任何事情,它仍然是空数组,我很坚持这一点,所以欢迎任何建议。这是相关代码:
/* flashMessagesList 组件 */
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import FlashMessage from './FlashMessage';
import { deleteFlashMessage } from '../../actions/actionFlashMessage';
class FlashMessagesList extends Component { //this is conected component cause we need data from store
render() {
const messages = this.props.messages.map(message =>
<FlashMessage key={message.id} message={message} deleteFlashMessage={this.props.deleteFlashMessage}/>
);
return (
<div>{messages}</div>
);
}
}
function mapStateToProps(state) {
return { //here we take a slice of global state
messages: state.flash.messages //define this in root reducer
};
}
function mapDispatchToProps(dispatch) {
return {
deleteFlashMessage: () => dispatch(deleteFlashMessage())
};
}
FlashMessagesList.propTypes = { //what this component will take
messages: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
style: PropTypes.string.isRequired,
text: PropTypes.string.isRequired
})
),
deleteFlashMessage: PropTypes.func.isRequired
};
export default connect(mapStateToProps, mapDispatchToProps)(FlashMessagesList); //pass message from store to this component
/* flashMessage 组件 */
import React, { Component, PropTypes } from 'react';
import classnames from 'classnames';
class FlashMessage extends Component {
deleteFlashMessage = () =>{
this.props.deleteFlashMessage(this.props.message.id); //dispatch some action that it has on it props
}
render() {
const { id, style, text } = this.props.message; //we can deconstruct message cause it is object
return (
<div className={classnames('alert', {
'alert-success': style === 'success',
'alert-danger': style === 'danger'
})}
>
{ text }
<button className="close" onClick={this.deleteFlashMessage}><span>×</span></button>
</div>
);
}
}
FlashMessage.propTypes = {
message: PropTypes.object.isRequired,
deleteFlashMessage: PropTypes.func.isRequired
};
export default FlashMessage;
/* reducerFlashMessage */
import * as types from '../actions/actionTypes';
import deepFreeze from 'deep-freeze';
import expect from 'expect';
const INITIAL_STATE = {
messages: []
};
const reducerFlashMessage = (state = INITIAL_STATE, action) => {
switch (action.types) {
case types.ADD_FLASH_MESSAGE:
return { ...state,
messages: [ ...state.messages, {
id: action.message.id,
style: action.message.style,
text: action.message.text
}]
};
}
return state;
};
export default reducerFlashMessage;
/* actionflashMessage */
import * as types from './actionTypes';
export function addFlashMessage(message) {
return {
type: types.ADD_FLASH_MESSAGE,
message
};
}
/* registerUser-我发送 addFlashMessage 的地方 */
export function registerUser({
timezone,
name,
email,
password,
passwordConfirmation
}) {
return dispatch => {
dispatch(isLoading(true));
axios.post('/auth/signup', {
timezone,
name,
email,
password,
passwordConfirmation
})
.then(response => {
dispatch(_registerUserSuccess(response.data.errors, response.data.message));
dispatch(isLoading(false));
dispatch(addFlashMessage(response.data.message));
dispatch(formReset());
// browserHistory.push('/signin');
setTimeout(() => {
// dispatch(_hideNotifications());
}, 2000);
// document.getElementById('form').reset();
})
.catch(error => {
if (error.response) {
dispatch(registerUserFailure(error.response.data.errors, error.response.data.message));
dispatch(isLoading(false));
dispatch(addFlashMessage(error.response.data.message));
}
else {
// Something happened in setting up the request that triggered an Error
console.log(error.message);
}
});
};
}
/* validation.js 来自我创建消息对象的服务器*/
export function validateSignupForm(payload) {
const errors = {};
const message = {};
if(validator.isEmpty(payload.name)) {
errors.name = 'Name is required';
}
if(validator.isEmpty(payload.email)) {
errors.email = 'Email is required';
}
if(validator.isEmpty(payload.password)) {
errors.password = 'Password is required';
}
if(validator.isEmpty(payload.passwordConfirmation)) {
errors.passwordConfirmation = 'Password confirmation is required';
}
if(validator.isEmpty(payload.timezone)) {
errors.timezone = 'Timezone is required';
}
if (payload.email && !validator.isEmail(payload.email)) {
errors.email = 'Please provide a correct email address.';
}
if (payload.password && payload.password.length < 4) {
errors.password = 'Password must have at least 4 characters.';
}
if (payload.passwordConfirmation && !validator.equals(payload.password, payload.passwordConfirmation)) {
errors.password = 'Passwords must match.';
}
if (!isEmpty(errors)) {
message.id = shortid.generate(),
message.style = 'danger';
message.text = 'Check the form for errors.';
}
return {
success: isEmpty(errors),
message,
errors
};
}
你的减速器有错字。而不是
switch (action.types) {
应该阅读
switch (action.type) {