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.
Related
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] });
};
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}
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
I have a root component as:
const EnterMobileNumberPage: React.FC = () => {
return (
<div
className="Page"
id="enterMobileNumberPage"
>
<CardView>
<p
className="TitleLabel"
>
Please enter your mobile number
</p>
<input
className="PlainInput"
type="text"
maxLength={10}
onChange={inputAction}
/>
<FilledButton
title="Next"
action={buttonAction}
invalid
/>
</CardView>
</div>
);
}
Where CardView and FilledButton are my custom components. FilledButton has logic shown below:
type FilledButtonProps = {
title: string,
bgcolor?: string,
color?: string,
invalid?: boolean,
action?: ()=>void
}
const FilledButton: React.FC<FilledButtonProps> = (props) => {
const [, updateState] = React.useState();
const forceUpdate = React.useCallback(() => updateState({}), []);
let backgroundColor: string | undefined
if(props.bgcolor){
backgroundColor = props.bgcolor
}
if(props.invalid === true){
backgroundColor = "#bcbcbc"
}
const overrideStyle: CSS.Properties = {
backgroundColor: backgroundColor,
color: props.color
}
return (
<a
className="FilledButton"
onClick={props.action}
>
<div style={overrideStyle}>
{props.title}
</div>
</a>
);
}
Here, I want to listen to text change event in input element. What should I write so that inputAction has a way to update FilledButton?
For example, I may want to change FilledButton's invalid to false when input element has 10 digits number.
(I didn't introduce Redux, since I'm quite a beginner)
so if you want to update the props receibe by <FilledButton />, you only need to store a state (call it action, maybe) when your inputAction onChange function is trigger, that way you'll update that state and that state is been pass to you children component:
import React, { useState } from 'react';
const EnterMobileNumberPage: React.FC = () => {
const [action, setAction] = React.useState('');
const handleChange = e => {
if (e && e.target && e.target.value) {
setAction(e.target.value);
}
};
return (
<div
className="Page"
id="enterMobileNumberPage"
>
<CardView>
<p className="TitleLabel" >
Please enter your mobile number
</p>
<input
className="PlainInput"
type="text"
maxLength={10}
onChange={handleChange}
/>
<FilledButton
title="Next"
action={buttonAction}
invalid={action.length === 10}
/>
</CardView>
</div>
);
}
Then, you'll have an action estate, that you could use to block your <FilledButton />and also to use it as the <input /> value, Hope this helps.
Since you want to update sibling component, the only way you have is to re-render parent component and pass updated prop to sibling component so it will also update.
const EnterMobileNumberPage: React.FC = () => {
const [mobileVal, inputAction] = React.useState('');
return (
<div>
<input
className="PlainInput"
type="text"
maxLength={10}
onChange={inputAction} // Updating parent component state on change
/>
<FilledButton
title="Next"
action={buttonAction}
invalid
mobileLength={mobileVal.length} // New prop for filled button
/>
</div>
);
}
The Problem
I have a form to send data through a api rest in React, the render and the writing on the form is very slow when I have about 80 text fields.
I'm using functional components with hooks to handle the input texts and Material-UI as UI framework.
In a first try, I had a currying function to handle the values:
setValue = (setter) => (e) => { setter(e.target.value) }
But the render process was really slow (because I was creating a function in every render), So I send the setter function as a prop, then it improves a little but not enough.
Actually the input response when I write a key in any input, it's about 500 ms.
What can I do to get a better performance?
The code was simplified for understanding purposes.
Sample code below:
const [input1, setInput1] = useState('')
const [input2, setInput2] = useState('')
const [input3, setInput3] = useState('')
.
.
.
const [input80, setInput80] = useState('')
// render the Inputs
<Input value={input1} setter={setInput1} text="Some label text" />
<Input value={input2} setter={setInput2} text="Some label text" />
<Input value={input3} setter={setInput3} text="Some label text" />
.
.
.
<Input value={input80} setter={setInput80} text="Some label text" />
My Input components:
const Input = ({
value, setter, text, type = 'text'
}) => {
const handleChange = (e) => {
const { value: inputValue } = e.target
setter(inputValue)
}
return (
<Grid>
<TextField
fullWidth
type={type}
label={text}
value={value}
onChange={handleChange}
multiline={multiline}
/>
</Grid>
)
}
All input values are must be in a component because I'm need to send them to a server with axios.
It looks like the Material-UI Input component is a bit heavy.
I have a sample codesandbox here where I initialised around 1000 inputs. Initially it lagged and crashed.
To begin with I added a memo to the Input component. This memoizes all the Input components, triggering a new render only if one of its props has changed.
For a start just add a memo to your input component.
import React, { memo } from 'react';
const Input = memo(({
value, setter, text, type = 'text'
}) => {
const handleChange = (e) => {
const { value: inputValue } = e.target
setter(inputValue)
}
return (
<Grid>
<TextField
fullWidth
type={type}
label={text}
value={value}
onChange={handleChange}
multiline={multiline}
/>
</Grid>
)
})
Note: If you have a custom setter like in your first case setValue = (setter) => (e) => { setter(e.target.value) }, you can wrap that in a useCallback to prevent multiple functions to be created for every render.