可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am trying out React/Redux/JS for the first time. I am a little confused about setting state in component vs letting redux update it.
I want to click a button to set the lightOn to true and have the updated this.props.lightOn value display. I am missing something fundamental but not sure what.
action:
export const setLight = lightStatus => dispatch => {
const res = lightStatus;
console.log(res); // => true on click
dispatch({ type: SET_LIGHT, payload: res });
};
In the component, {this.props.onLight} is undefined so I am not updating state correctly:
import React, { Component } from 'react';
import { connect } from 'react-redux';
//import * as actions from '../actions';
import { setLight } from '../actions';
import { bindActionCreators } from 'redux';
class Light extends Component {
render() {
return (
<div>
Light Status: {this.props.lightOn}
<button
className="btn"
onClick={() => this.props.setLight(true)}
>
Set Light!
</button>
</div>
);
}
}
function mapStateToProps(state) {
return {
lightOn: state.lightOn
};
}
function mapDispatchToProps(dispatch) {
//whenever selectBook is called the result should be passed to all of our reducers
return bindActionCreators({ setLight: setLight }, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Light);
the reducer (updated with spread operator):
import { SET_LIGHT } from '../actions/types';
export default function(state = [], action) {
switch (action.type) {
case SET_LIGHT:
return { ...state, lightOn: action.payload };
default:
return state;
}
}
回答1:
How about set it as a state instead of pass direct to the function ? First binding it inside Constructor and then on click setState to true ?
constructor(props) {
super(props);
this.state:{lightOn: false};
this.OnClick = this.OnClick.bind(this);
}
onClick(event) {
this.setState({lightOn: true});
}
onClick ={this.onClick()}
As you are using redux , you can do it this way by returning a fresh state instead
return {...state, action.payload };
回答2:
How it works?
Through connect
you can inject props that are used to control redux state and bind it to the component. For example:
class SomeComponent extends React.Component {
componentWillMount() {
this.props.sendSomething('someName');
}
render() {
return (
<div>{this.props.stateProp}</div>
)
}
}
const mapStateToProps = state = ({
stateProp: state.someReduxState,
});
const mapDispatchToProps = dispatch => ({
sendSomething: (name) => dispatch(someActionCreator(name)),
});
const ConnectedComponent = connect(
mapStateToProps,
mapDispatchToProps,
)(SomeComponent);
In given example you are binding someReduxState
part of state to stateProp
, which is injected into component. You are injecting method (sendSomething
) that will dispatch and action in redux. If component relays on displaying part of redux state, it will be updated each time redux state changes, because props that are injected to component will change. You can find more in official Redux documentation here, or you can watch free tutorial on egghead here.
回答3:
You are not returning a new state here
export default function(state = [], action) {
switch (action.type) {
case SET_LIGHT:
return action.payload;
default:
return state;
}
}
You're directly returning action.payload
which is simply lightStatus
Which means you can't access state.lightOn
in your mapStateToProps
Your should be doing
case SET_LIGHT:
return {
...state,
lightOn: action.payload,
};
Or directly
case SET_LIGHT:
return {
...state,
action.payload,
};
This way the entire state is not lost and its not directly mutating the state.
Here i am using the object spread operator instead of Object.assign for the sake of simpicity.
回答4:
the wrong on the reducer, the reducer should return the state object with lightOn key, but you return payload value, and this is the issue.
your reducer should return lightOn with payload value like this.
import { SET_LIGHT } from '../actions/types';
export default function(state = {}, action) {
switch (action.type) {
case SET_LIGHT:
return {...state, action.payload}
default:
return state;
}
}
回答5:
Instead of setting state with array I am now setting with object. I guess I was putting lightOn object in light array which was making it inaccessible.
action:
export const setLight = lightStatus => dispatch => {
dispatch({ type: SET_LIGHT, payload: lightStatus });
};
reducer:
import { SET_LIGHT } from '../actions/types';
export default function(state = { on: false }, action) {
console.log('state ', state);
switch (action.type) {
case SET_LIGHT:
return { ...state, on: action.payload };
default:
return state;
}
}
the component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
//import * as actions from '../actions';
import { setLight } from '../actions';
import { bindActionCreators } from 'redux';
class Light extends Component {
render() {
return (
<div>
Light On: {this.props.light.on.toString()}
<button
className="btn"
onClick={() => this.props.setLight(!this.props.light.on)}
>
Set Light!
</button>
</div>
);
}
}
function mapStateToProps(state) {
return { light: state.light };
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ setLight: setLight }, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Light);