if (this.state.height !== prevState.height || this.state.hair !== prevState.hair || this.state.weight !== prevState.weight || this.state.eyes !== prevState.eyes || this.state.activity !== prevState.activity || this.state.gender !== prevState.gender || this.state.age !== prevState.age || this.state.wage !== prevState.wage || this.state.city !== prevState.city || this.state.disability !== prevState.disability) {
this.setState({
person: {
height: this.state.height, hair: this.state.hair, weight: this.state.weight, city: this.state.city, eyes: this.state.eyes, disability: this.state.disability,
},
});
this.setState({ showSave: this.state.height.length > 0 && this.state.hair.length > 0 && this.state.activity.length > 0 && this.state.gender.length > 0 && this.state.age.length > 0 && this.state.wage.length > 0 && this.state.overtime.length > 0 && this.state.allowance.length > 0 });
}
What I'm doing is saving the state of a form, the state person
has the fields that are set above whenever one of them change, and the state showSave
depends of some of those fields and some others, which is also updated if any of the above fields change.
Also, the state of the class has more fields that are not needed in this if
.
I'm looking for a cleaner / easier to read way of making all these comparations without needing to add all of this.
CodePudding user response:
/**
* Factory function to define which properties in state you want to compare
* @param {string[]} keys the keys in state you want to compare
* @returns {(state, prevState) => boolean} function to compare state instances
*/
const makeCompareState = (keys) => {
/**
* @param {state} current state instance
* @param {prevState} previous state instance
* @returns {boolean} true if properties in state are unchanged, false otherwise
*/
const compareState = (state, prevState) => {
return keys.every(key => state[key] === prevState[key])
}
return compareState;
};
// Define the keys of state you want to compare
const compareState = makeCompareState(["height", "weight", "hair", "city"])
if(compareState(state, prevState) {
// state has not changed
} else {
// state has changed
}
Note, this solution only works for primitive value since it does a shallow comparison, ie. this will not work for comparing objects, functions, or arrays.
Alternatively, you can use a third-party solution if you don't mind the extra bundle size, like dequal
, to do a deep comparison of state
import { isEqual } from "dequal";
if(isEqual(state, prevState)) {
// state is unchanged
} else {
// state has changed
}
CodePudding user response:
You can use loadash library isEqual method for this. It deep compare 2 objects.This way it is more cleaner.
const _ = require('lodash');
if(!_.isEqual(this.state, prevState)){//...}
CodePudding user response:
Your state is inconsistent. It's signature is either
{
gender:any,
height:any,
hair:any,
weight:any,
city:any,
eyes:any,
allowance:any,
wage:any,
overtime:any,
disability:any
}
or
{
showSave:boolean
}
I would strongly encourage you upgrade to at least React 16.8, convert your app into functional components wherever possible, and use useReducer
instead.
const INITIAL_STATE = {
gender: '',
height: '',
hair: '',
weight: '',
city: '',
eyes: '',
allowance: '',
wage: '',
overtime: '',
disability: '',
showSave: false
};
const characterStateReducer = (state, action) => {
if (action.type === 'set' && action.attributes) {
// copy from previous state
const newState = { ...state };
for (const attr in action.attributes) {
newState[attr] = action.attributes[attr];
}
// update showSave
newState.showSave =
newState.height.length > 0 &&
newState.hair.length > 0 &&
newState.activity.length > 0 &&
newState.gender.length > 0 &&
newState.age.length > 0 &&
newState.wage.length > 0 &&
newState.overtime.length > 0 &&
newState.allowance.length > 0;
return newState;
} else {
// NOTE: returning same state won't trigger updates
return state;
}
}
and use this as
const [state, dispatch] = useReducer(characterStateReducer, INITIAL_STATE);
where dispatch is called like
dispatch({
type:'set',
attributes: {
height:"5'10''"
}
});