I take a button's name from onClick and work out if the button pressed is the correct answer. I want to dynamically change the hook based on this value passed in. I am using react hooks to change the class to display if it's incorrect or correct. Before I used class components and used this:
this.setState({
[button]: 'btn-success',
Currently I have something ugly like this:
if (button.includes('1')) {
setBtn1('btn-success');
} else if (button.includes('2')) {
setBtn2('btn-success');
} else if (button.includes('3')) {
setBtn3('btn-success');
} else if (button.includes('4')) {
setBtn4('btn-success');
}
Any help is appreciated, thank you.
CodePudding user response:
If I'm reading this correctly (there is some context missing), you should be able to do this:
const [btns, setBtns] = useState({});
// then, in your handler function
setBtns({...btns, [button]: 'btn-success'})
// or (thanks for the comment) as a callback
setBtns((btns)=>({...btns, [button]: 'btn-success'}))
You can see the only thing that changes is that you have to set the entire state to a new object which is accomplished by spreading the old state and then setting the new key afterwards.
CodePudding user response:
As Taxel said, you can use an object in a state member, which would be similar to what you used with class components. That's probably the best approach for what you describe.
If you don't want to do that, you can also do this with individual state members:
const buttons = {
btn1: useState(""), // btn1[0] is the state, btn1[1] is the setter
btn2: useState(""), // btn2[0] is the state, btn2[1] is the setter
btn3: useState(""), // ...
btn4: useState(""), // ...
};
// ...
buttons[button][1]("btn-success");
Or slightly more exotically with destructuring:
const buttonStates = {};
const buttonSetters = {};
({0: buttonStates.btn1, 1: buttonSetters.btn1} = useState(""));
({0: buttonStates.btn2, 1: buttonSetters.btn2} = useState(""));
({0: buttonStates.btn3, 1: buttonSetters.btn3} = useState(""));
// ...
buttonSetters[button]("btn-success");
Live Example:
const {useState} = React;
const Example = () => {
const buttonStates = {};
const buttonSetters = {};
({0: buttonStates.btn1, 1: buttonSetters.btn1} = useState(""));
({0: buttonStates.btn2, 1: buttonSetters.btn2} = useState(""));
({0: buttonStates.btn3, 1: buttonSetters.btn3} = useState(""));
const setButton = (button, value) => {
buttonSetters[button](value);
};
return <div>
<div>
btn1: {buttonStates.btn1}
<div>
<input type="button" value="Set btn-success" onClick={() => setButton("btn1", "btn-success")}/>
<input type="button" value="Set btn-error" onClick={() => setButton("btn1", "btn-error")}/>
</div>
</div>
<hr/>
<div>
btn2: {buttonStates.btn2}
<div>
<input type="button" value="Set btn-success" onClick={() => setButton("btn2", "btn-success")}/>
<input type="button" value="Set btn-error" onClick={() => setButton("btn2", "btn-error")}/>
</div>
</div>
<hr/>
<div>
btn3: {buttonStates.btn3}
<div>
<input type="button" value="Set btn-success" onClick={() => setButton("btn3", "btn-success")}/>
<input type="button" value="Set btn-error" onClick={() => setButton("btn3", "btn-error")}/>
</div>
</div>
</div>;
buttonSetters[button]("btn-success");
};
ReactDOM.render(<Example />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
But without a powerful reason for wanting individual state members for each button, I'd go with the approach Taxel showed.