问题
I am using redux and I have a state with empty string values. Each empty key has to be filled in using input onChange event. Say I want to change 'business_selected' key, what should the function look like? Here's my state:
const initialState = {
user: {},
applications: [
{
companyFlow: {
stage1: {
business_activity: "",
business_selected: ""
},
stage2: {
legal_name: "",
registration_number: "",
business_website: "",
incorporation_date_text: "",
legal_form: ""
}
}
}
]
};
export default function changeInput(state = initialState, action) {
switch (action.type) {
case CHANGE_INPUT:
return {
// ??????
};
default:
return state;
}
}
回答1:
onChnage values should be stored in local state and not in redux state.
Only global values should be stored in redux state ideally.
回答2:
Left aside concerns about deeply nested state variables (which is recommended to avoid) and taking for granted your rationale behind having those variables globally accessible, if what you're asking, at the very basic level, is how to bind dispatching certain actions to component events (e.g. properties like onChange, onKeyUp, etc), I would recommend to refer to official tutorial on that topic.
However, at its very simple, the following example demonstrated the general concept. You may scale, rebalance that between state complexity and components flexibility on your own discretion.
//dependencies
const { render } = ReactDOM,
{ createStore } = Redux,
{ connect, Provider } = ReactRedux
//store initialization
const defaultState = {
businessActivity: {
options: ['operation', 'investing', 'financing'],
currentValue: ''
},
legalName:{
currentValue:''
}
},
appReducer = (state=defaultState, action) => {
switch(action.type){
case 'SET_PARAM': {
const {payload:{param,val}} = action
return {...state, [param]:{...state[param], currentValue: val}}
}
default: return state
}
},
store = createStore(appReducer)
//arbitrary dropdown component
const DropDown = ({onParamChange, param, options, val}) => (
<select
param={param}
onChange={e => (val=e.target.value, onParamChange(param,val))}
>
{['',...options].map((opt,key) => <option value={opt} key={key} selected={opt==val}>{opt}</option>)}
</select>
)
//dropdown container
var mapStateToProps = (state, {param}) => ({
val:state[param]['currentValue']||'',
options:[...state[param]['options']]
}),
mapDispatchToProps = (dispatch, {param,val}) => ({onParamChange: (param,val) => dispatch({type:'SET_PARAM',payload:{param,val}})})
const DropDownContainer = connect(mapStateToProps, mapDispatchToProps)(DropDown)
//arbitrary input component
const InputBox = ({onParamInput, param, val}) => (
<input
param={param}
val={val}
placeholder={val}
onKeyUp={e => (val=e.target.value, onParamInput(param,val))}
></input>
)
//input box container
var mapStateToProps = (state, {param}) => ({val: state[param]['currentValue']||''}),
mapDispatchToProps = (dispatch, {param,val}) => ({onParamInput: (param,val) => dispatch({type:'SET_PARAM', payload:{param,val}})})
const InputBoxContainer = connect(mapStateToProps,mapDispatchToProps)(InputBox)
//resulting state mirror
const PrintOut = ({activity,name}) => <div>Current business activity is {activity||'_'} and legal name is {name||'_'}</div>
//print out container
var mapStateToProps = ({businessActivity:{currentValue:activity},legalName:{currentValue:name}}) => ({activity,name})
const PrintOutContainer = connect(mapStateToProps)(PrintOut)
//provider
render(
<Provider store={store}>
<DropDownContainer param="businessActivity" />
<InputBoxContainer param="legalName" />
<PrintOutContainer />
</Provider>,
document.getElementById('root')
)
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script><script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script><script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.4/redux.min.js"></script><script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.1.1/react-redux.min.js"></script><div id="root"></div>
回答3:
Normally onChange is handled using the local state in the component itself but if you want to do it in redux you could do something like this.
export default function changeInput(state = initialState, action) {
switch (action.type) {
case CHANGE_INPUT:
return {
...state,
applications: state.applications.map(application => {
return Object.assign({}, application, {
...application,
companyFlow: {
...application.companyFlow,
stage1: {
...application.companyFlow.stage1,
business_selected: action.payload
}
}
}
return application
});
default:
return state;
}
}
来源:https://stackoverflow.com/questions/59300316/change-redux-state-based-on-onchange-event-value