How to combine two functions but change two separate states individually - javascript

I have two items which have separate states and a function which toggles their state. How can I change the state of each item using only one function?
The items:
<Form.Check
type="switch"
id="smsNotificationSwitch"
label="SMS Notifications"
checked={this.state.smsNotifications}
onChange={this.toggleSMSSwitch}
/>
<Form.Check
type="switch"
label="Email Notifications"
id="emailNotificationSwitch"
checked={this.state.emailNotifications}
onChange={this.toggleEmailSwitch}
/>
The state:
state = {
smsNotifications: true,
emailNotifications: true,
}
the functions I would like to combine:
toggleSMSSwitch = () => {
this.setState((prevState) => (
{smsNotifications: !this.state.smsNotifications}));
};
toggleEmailSwitch = () => {
this.setState((prevState) => (
{emailNotifications: !this.state.emailNotifications})
);
};
I am asking to learn more than anything else.

You can add name attribute as the same as the state variable name. On onChange handler you can get name from the event param.
<Form.Check
name="smsNotifications"
type="switch"
id="smsNotificationSwitch"
label="SMS Notifications"
checked={this.state.smsNotifications}
onChange={this.toggleSwitch}
/>
<Form.Check
name="emailNotifications"
type="switch"
label="Email Notifications"
id="emailNotificationSwitch"
checked={this.state.emailNotifications}
onChange={this.toggleSwitch}
/>
toggleSwitch = event => {
const { name } = event.target;
this.setState({ [name]: !this.state[name] });
};

Related

Set the react-bootstrap checkbox to uncheck state

I am a newbie to react and I was not able to figure how to reset the checkbox to uncheck stat on resetting. I have used the react-bootstrap form and need to reset the checkboxes to default state on a button click.
export const pitch: FC<pitchProps> = ({ pitchData, totalNoOfData }) => {
const [loading, setLoading] = useState(false);
const [totalRows, setTotalRows] = useState(totalNoOfData);
const formData = {
searchData: pitchParam.searchString,
searchCriteria: pitchParam.searchStatus,
};
const handleFormData = (e: any) => {
if (e.target.type === "checkbox") {
e.target.checked
? formData.searchCriteria.push(e.target.id)
: (formData.searchCriteria = formData.searchCriteria.filter(
(item: any) => item !== e.target.id
));
} else {
formData.searchData = e.target.value;
}
};
return (
<div className={styles.mainTableContainer}>
<div className="d-flex ml-2">
<Form className="w-100">
<Form.Row>
<Form.Label className={styles.label}>NEW </Form.Label>
<Form.Check
className={styles.checkbox}
type="checkbox"
id="OLD"
onChange={(e: any) => handleFormData(e)}
/>
<Form.Label className={styles.label}> OLD </Form.Label>
<Form.Check
className={styles.checkbox}
type="checkbox"
id="OUTDATED"
onChange={(e: any) => handleFormData(e)}
/>
<Form.Label className={styles.label}>OUTDATED </Form.Label>
<Form.Check
className={styles.checkbox}
type="checkbox"
id="SUCCESS"
onChange={(e: any) => handleFormData(e)}
/>
</Form.Row>
</Form>
</div>
</div>
);
};
Your checkbox component is not fully controlled by you, so it is an uncontrolled component.
Solution 1 : using reference
As your checkbox is controlled by the bootstrap-form you need to create a reference and pass it to the checkbox to manipulate the checked value:
import React, {useRef} from 'react';
// reset of the codes
// in the pitch component
const checkboxRef = useRef(null);
Now, pass the checkboxRef into the checkbox component within ref property:
<Form.Check
type={type}
id={`default-${type}`}
label={`default ${type}`}
ref={checkboxRef} // <--------- here
/>
Now, try to add another button with a reset handler:
const handleResetCheckbox = () => {
checkboxRef.current.checked = false
}
Solultion 2 : using react key
You can also do the same with a different approach by using the React component's key.
If the key gets changes, react will remove the component and create a new one. so you can use this key property to uncheck (reset) the checkboxes:
define a key with useState and pass it to the checkbox component:
const [key, setKey] = useState(false)
<Form.Check
key={key} // <--------- added here
type={type}
id={`default-${type}`}
label={`default ${type}`}
/>
Now, in the handleResetCheckbox, change the key:
const handleResetCheckbox = () => {
setKey(prevState => !prevState)
}
So, when you reset the checkbox, the key will change then React will remove the old checkbox and render a new one. more information in React documentation.

Could not console log the updated state array

I am making a simple application where the user can create a new form on a button click, so for this, I have an array state like this :
const [numbers, setNumbers] = useState([0]);
const [count, setCount] = useState([0]);
And on my button onClick method I have this,
setCount(count + 1);
setNumbers(numbers.concat(numbers[0] + count));
In my render method, I have :
{numbers.map((number) => {
return (
<div key={number}>
<InputCreator id={number} value={number} />
</div>
);
})}
And my InputCreator component is a simple callback component with few textfields.
So far, it works well. Lately I wanted to add a delete functionality where the user can delete that particular form. So, I added.a button inside this form and on the onClick method, I tried console loging the "numbers" state to check everything is working, but it logs only the default value I have given while creating the state and not the updated state. What could be the reason ? So my idea is to delete that index from the array using the props passed, so that the component will re-render with the updated number of forms. Is there a better way to do this ?
EDIT : This is my InputCreator component,
const InputCreator = useCallback((props) => {
const { id, value } = props;
// console.log(titles[id]);
return (
<div>
<Col xs={12} md={8}>
<div className={styles.container}>
<Form
noValidate
validated={false}
onSubmit={handleSubmit}
encType="multipart/form-data"
>
<Form.Group controlId="formGroupTitle">
<Form.Label>Title</Form.Label>
<Form.Control
type="text"
placeholder="Title"
onChange={(e) => handleChange(e, value)}
name="title"
value={titles[id]}
/>
</Form.Group>
<Form.Group controlId="formGroupTitle">
<Form.Label>Description</Form.Label>
<Form.Control
type="text"
name="description"
placeholder="Max limit 30"
onChange={(e) => handleChange(e, value)}
maxLength={31}
value={descriptions[id]}
/>
</Form.Group>
<Button
variant="outline-primary"
size="sm"
className={styles.deleteBtn}
onClick={(e) => handleDelete(e, number)}
>
X
</Button>
</Form>
)})
handleDelete :
const handleDelete = (e, value) => {
e.preventDefault();
console.log(numbers);
}
I will just point out the mistakes I see as your question is not worded clearly.
Keys aren't passed into they are passed into list in order to create a list for every element with a unique ID. I am not sure what you are trying to do in your render method but I would suggest doing something like this :
const renderNumbers = () => myArray.map((number, index) =>
<MyComponent key={index} number={number} />
To delete an element. Create a function that takes in the ID. Filter it to show a new array without that ID, below is an example that you ca apply.
const deletePerson = (id) => {
setNumbers(numbers.filter((number) => number.id !== id));
};
Send this handle to the button that deletes the element
const handleDeleteNumber = () => {
deleteNumber(number.id);
};
Make a single handleInputChange() that you can use for all input changes. Such as this one :
const handleInputChange = (event) => {
setInfo({ ...info, [event.target.name]: event.target.value });
};
In the tag you pass in id and values separately like this
value={descriptions}
key={id}

How to set the handler for multiple checkboxes in react?

I've created multiple checkboxes in my small project. I'm facing a problem in retrieving the info of the checkbox while giving a checkmark. if I give a checkmark on toys then the value should be toys or if I give checkmark on both toys and lights i should get both toys and lights in the console.
Here is code:
import React from "react";
import { render } from "react-dom";
import { Segment, Form, Checkbox, Button } from "semantic-ui-react";
import axios from "axios";
export default class ShowForm extends React.Component {
constructor(props) {
super(props);
this.state = {
event: ""
};
}
handleCheck = event => {
console.log("Hello", event);
};
render() {
return (
<div>
<Segment>
<Form>
<Checkbox label="Cake" onChange={this.handleCheck} />
<br />
<Checkbox label="Lights" onChange={this.handleCheck} />
<br />
<Checkbox label="Flowers" onChange={this.handleCheck} />
<br />
<Checkbox label="Toys" onChange={this.handleCheck} />
<br />
<Button onClick={this.handleSubmit}> Submit </Button>
</Form>
</Segment>
</div>
);
}
}
Here is whole code: "https://codesandbox.io/s/zealous-paper-p9zb5"
Can anyone please help me in this issue?
You need an array to store all the checked status.
handleCheck = id => () => { // Get indentify from each element
const { checkedList } = this.state;
const result = checkedList.includes(id) // Check if checked at this moment
? checkedList.filter(x => x !== id) // If checked, remove
: [...checkedList, id]; // If not, add
this.setState({ checkedList: result }, () => { // setState
console.log(this.state.checkedList); // setState is async, log in callback
});
};
And if you want, you can make the Checkbox component a common one so you don't need to bind the label in three places in each of it.
<Checkbox
label="Cake"
onChange={this.handleCheck("Cake")}
checked={checkedList.includes("Cake")} // If included, checked
/>
Another minimum reproducible demo
Try it in-text:
const list = [...Array(10).keys()].map(x => ({ id: x }));
const App = () => {
const [selected, setSelected] = React.useState([]);
const onChangeHandler = id => () => {
selected.includes(id)
? setSelected(selected.filter(x => x !== id))
: setSelected([...selected, id]);
};
const onRemove = () => {
setSelected([]);
};
return (
<div className="App">
{list.map(item => (
<input
type="checkbox"
key={item.id}
checked={selected.includes(item.id)}
onChange={onChangeHandler(item.id)}
/>
))}
<br />
<button onClick={onRemove}>Remove all</button>
<div>{selected.join(",")}</div>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
Pretty sure, you can ES6 named properties and arrow functions to create a clean flow
Consider a state:
this.state={
isToy: false,
isCake: false,
isFlower: false,
isLight: false
}
Add a property of Name. These names should ideally match the ones in state.
<Form>
<Checkbox name="isCake" label="Cake" checked={this.state.isCake} onChange={this.handleCheck} />
<Checkbox name="isLight" label="Lights" checked={this.state.isLight} onChange={this.handleCheck} />
<Checkbox name="isFlower" label="Flowers" checked={this.state.isFlower} onChange={this.handleCheck} />
<Checkbox name="isToy" label="Toys" checked={this.state.isToy} onChange={this.handleCheck} />
<Button onClick={this.handleSubmit}> Submit </Button>
</Form>
Then write the handleCheck arrow function, using ES6 name properties feature to access state property:
handleCheck = (event) => {
let name = event.target.name
this.setState{[name]: !this.state[name]}
}
References:
React Controlled Forms: https://reactjs.org/docs/forms.html
Semantic UI Checkboxes: https://react.semantic-ui.com/modules/checkbox/#usage-remote-control

(React) How to make checkbox UI look checked after mutating state?

How to change the UI of the CheckBox component? State mutates finely as I wanted, however the checkbox doesn't look checked after the onCheck call (when click it).
I guess I must create or use an existing props for input objects:
onCheck = item => {
this.setState(prevState => ({
usage: prevState.usage.concat(item.label, ",")
}));
//Todo: Display as checked
};
{checkboxes.map(item => (
<div className="d-inline-block w-50 p">
<CheckBox
name={item.name}
onChange={this.onCheck.bind(this, item)}
/>
<label key={item.key}>{item.label}</label>
</div>
))}
const CheckBox = ({ type = "checkbox", name, checked = false, onChange }) => (
<input type={type} name={name} checked={checked} onChange={onChange} />
);
Welcome to the community, you need to pass checked true/false to the CheckBox component
<CheckBox
name={item.name}
checked={this.state.usage.includes(item.label)}
onChange={this.onCheck.bind(this, item)}
/>
Also you don't need to include ',' while concat as it is creating an array
Refer link for reference https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat
You need to include the checked property and you can use a state to handle it. Let's say "isChecked".
Example:
<CheckBox checked={this.state.isChecked} \> (+ your previously added properties)
And you'd include something like this in your function:
this.setState((prevState) => { isChecked: !prevState.isChecked});

React: How to set state of object property?

I am a beginner with React and I have been using the NPM package react-
tabs in order to display tabs on my site.
I have been trying to allow the
user to update descriptions of their profiles using a checkbox to open an input type=text tag allowing the user to put in their own details for their profile.
However, I am struggling to set the state of the description for the tab using the handleDesc function. I am not sure how I can update the state of the descriptions for each tab. I keep getting the error "A component is changing a controlled input of type checkbox to be uncontrolled."
Can anyone help me?
class EditSite extends React.Component {
constructor(props) {
super(props);
this.state = {About: true,
"Opening Hours": true,
Contact: true,
Menu: false,
Offers: false,
"External Links": true,
tabOptions: {
About: {desc:'1'},
"Opening Hours": {desc: '2'},
Contact: {desc:'3'},
Menu: {desc:'4'},
Offers: {desc:'5'},
"External Links": {desc:'6'},
}
}
Below is the function (handleDesc) I am struggling with to set the state of the descriptions.
handleDesc = (event) => {
let tabOptions = {...this.state.tabOptions[event.target.name]};
tabOptions = event.target.value;
console.log(tabOptions);
this.setState({tabOptions: [event.target.name]});
}
render() {
const links = [];
const tabs = [];
const tabPanels = [];
The second input tag is where I would like the user to be able to add their own details to the specific tab.
Object.keys(this.state.tabOptions).forEach(name => {
links.push(
<div>
<label key={name}>{name}</label>
<input
type="checkbox"
checked={this.state[name]}
name={name}
onChange={this.handleCheckClicked}
/>
{ this.state[name] === true ? (
<input
name={name}
type='text'
onChange={this.handleDesc}
/>
) : null }
</div>
);
if (!this.state[name]) return;
const { desc } = this.state.tabOptions[name];
tabs.push(
<Tab>
<h3>{name}</h3>
</Tab>
);
tabPanels.push(
<TabPanel>
{desc}
</TabPanel>
);
});
SetState wants a new object. So settings just a property of a nested object isn’t possible in that way. What you could do is copying the tabOptions and changing the properties you want before you pass it to setState.
example
handleDesc = (event) => {
const newTabOptions = {
...this.state.tabOptions,
[event.target.name]: {desc: event.target.value}
this.setState({tabOptions: newTabOptions});
}
A quick way to resolve this would be to revise that code that binds the <input /> element's onChange handler to the following:
links.push(
<div>
<label key={name}>{name}</label>
<input
type="checkbox"
checked={this.state[name]}
name={name}
onChange={ (event) => this.handleCheckClicked(event) /* <-- update this also */ }
/>
{ this.state[name] === true ? (
<input
name={name}
type='text'
onChange={ (event) => this.handleDesc(event) /* [ UPDATE HERE ] */ }
/>
) : null }
</div>
);
By declaring an arrow function inline like this:
onChange={ (event) => this.handleDesc(event) }
it will set the context of the handleDesc function to be the <EditSite /> component (currently, the context will be the global window object). This means that when you access this.state, or call a method like this.setState() inside of the handleDesc, it will work as expected.
For more information on this subtle characteristic of "arrow functions" see this article.
Update
Also, consider updating your handleDesc method like so to correctly mutate your state:
handleDesc = (event) => {
let tabOptions = {...this.state.tabOptions[event.target.name]};
tabOptions = event.target.value;
console.log(tabOptions);
this.setState({tabOptions}); // <--- Update this line
}
Update 2
On further investigation (into react's source code), it would see the problem is that your <input /> element does not have a checked attribute when this.state[name] === true is satisfied. If you apply a dummy attribute/value pair of checked={ true } on the input rendered in the this.state[name] === true case, this warning message should stop showing:
links.push(
<div>
<label key={name}>{name}</label>
<input
type="checkbox"
checked={this.state[name]}
name={name}
onChange={this.handleCheckClicked}
/>
{ this.state[name] === true ? (
<input
name={name}
type='text'
checked={ true } /* <-- Update this line, don't include this comment in code */
onChange={this.handleDesc}
/>
) : null }
</div>
);

Categories

Resources