I want to update an array each time a checkbox is toggled to true. With this current code, if I click on a checkbox it will log that it's false. Even though I have just updated the state. Does setState just take some time, like an API call? That doesn't make sense to me.
import React, {Component} from 'react';
class Person extends Component {
constructor(props) {
super(props);
this.state = {
boxIsChecked: false
};
this.checkboxToggle = this.checkboxToggle.bind(this);
}
checkboxToggle() {
// state is updated first
this.setState({ boxIsChecked: !this.state.boxIsChecked });
console.log("boxIsChecked: " + this.state.boxIsChecked);
if (this.state.boxIsChecked === false) {
console.log("box is false. should do nothing.");
}
else if (this.state.boxIsChecked === true) {
console.log("box is true. should be added.");
this.props.setForDelete(this.props.person._id);
}
}
render() {
return (
<div>
<input type="checkbox" name="person" checked={this.state.boxIsChecked} onClick={this.checkboxToggle} />
{this.props.person.name} ({this.props.person.age})
</div>
);
}
}
export default Person;
I have tried onChange instead of onClick. I feel like I'm already following advice I've read about for the basic component formulation from here and here. Is the fact I'm using Redux for other values affecting anything? Is there a way to just read what the checkbox is, instead of trying to control it? (The checkbox itself works fine and the DOM updates wether it's checked or not correctly.)
I know that this thread is answered but i also have a solution for this, you see the checkbox didn't update with the provided value of setState, i don't know the exact reason for this problem but here is a solution.
<input
key={Math.random()}
type="checkbox"
name="insurance"
defaultChecked={data.insurance}
/>
by giving the key value of random the checkbox gets re-rendered and the value of the checkbox is updated, this worked for me. Also i am using hooks, but it should work for class based implementation to.
reference: https://www.reddit.com/r/reactjs/comments/8unyps/am_i_doing_stupid_or_is_this_a_bug_checkbox_not/
setState() is indeed not reflected right away:
Read here in the docs:
setState() enqueues changes to the component state and tells React
that this component and its children need to be re-rendered with the
updated state. This is the primary method you use to update the user
interface in response to event handlers and server responses.
Think of setState() as a request rather than an immediate command to
update the component. For better perceived performance, React may
delay it, and then update several components in a single pass. React
does not guarantee that the state changes are applied immediately.
setState() does not always immediately update the component. It may
batch or defer the update until later. This makes reading this.state
right after calling setState() a potential pitfall. Instead, use
componentDidUpdate or a setState callback (setState(updater,
callback)), either of which are guaranteed to fire after the update
has been applied. If you need to set the state based on the previous
state, read about the updater argument below.
setState() will always lead to a re-render unless
shouldComponentUpdate() returns false. If mutable objects are being
used and conditional rendering logic cannot be implemented in
shouldComponentUpdate(), calling setState() only when the new state
differs from the previous state will avoid unnecessary re-renders.
and here some experiments
So it might be better to catch the event and check it, and not depend on the this.setState()
Something like that:
handleChange: function (event) {
//you code
if (event.target.checked) {
console.log("box is true. should be added.");
this.props.setForDelete(this.props.person._id);
}
}
and
render() {
return (
<div>
<input type="checkbox" name="person" checked={this.state.boxIsChecked}
onChange={this.handleChange.bind(this)}/>
{this.props.person.name} ({this.props.person.age})
</div>
);
}
You can't access the updated value of state just after calling setState().
If you want to do stuff with updated value of state, you can do like this. i.e inside setState() callback function
checkboxToggle() {
// state is updated first
this.setState({ boxIsChecked: !this.state.boxIsChecked },()=>{
console.log("boxIsChecked: " + this.state.boxIsChecked);
if (this.state.boxIsChecked === false) {
console.log("box is false. should do nothing.");
}
else if (this.state.boxIsChecked === true) {
console.log("box is true. should be added.");
this.props.setForDelete(this.props.person._id);
}
});
React solution:
HTML:
<input type="checkbox" ref={checkboxRef} onChange={handleChange}/>
JS:
const checkboxRef = useRef("");
const [checkbox, setCheckbox] = useState(false);
const handleChange = () => {
setCheckbox(checkboxRef.current.checked);
};
Now your true/false value is stored in the checkbox variable
To expand no newbies answer; what worked for me:
remove preventDefault() from onChange Event-Listener but this didn't feel right for me since I want only react to update the value. And it might not apply in all cases like OPs.
include the checked value in the key like key={"agree_legal_checkbox" + legalcheckboxValue} checked={legalcheckboxValue}
I got both these solutions from newbies answer but having a math.random key just felt wrong. Using the value for checked causes the key only to change when when the checked value changes as well.
(not enough reputation to simply post this as a comment, but still wanted to share)
I arrived here trying to figure out why my checkbox, rendered using the HTM tagged template library, wasn't working.
return html`
...
<input type="checkbox" checked=${state.active ? "checked" : null} />
...
`;
It turns out I was just trying too hard with that ternary operator. This works fine:
return html`
...
<input type="checkbox" checked=${state.active} />
...
`;
Related
I've been experimenting with conditionally displaying components.
Externally handled:
{show && <MyComponent />}
Internally handled:
const MyComponent = () => {
const [externalState] = useContext();
const [state, setState] = useState("")
// Don't render base on some state value
if(externalState === false) return null;
return <input value={state} onChange={e=>setState(e.currentTarget.value)} type="text"/>
}
I've noticed that in the second method, when I toggle the Component on and off, the state does not reset. Is there a way to fix this or is the second approach not recommended.
The reason you'll see different behaviors with state retention is because the first method will mount or unmount MyComponent whenever the condition changes.
If show is false, MyComponent is not just hidden, it is actually removed from the DOM.
In the second method, only the JSX returned from MyComponent is removed from the DOM, the rest of the component remains mounted, meaning state is maintained.
As far as "fixing" the problem (most will actually see the loss of state a bug, so this is a different perspective), the simplest method is to continue using option 1. You can certainly use option 2, but you will have to add some extra logic/function calls to reset state each time the internally managed show is updated.
I cannot get my checkbox state to change. The default option in my Mongoose model is false. I can successfully get it to update the user's profile when they check it and submit only on the first time (it'll change to true), but when they go back to edit the profile (same routes) and $set the profile fields, it doesn't change the boolean value of the "displayEmailOnProfile" to false. It just remains checked. The console.logs() are kinda weird as well. When the box is checked, it prints out true and then the state for displayEmailOnProfile is false. When it's unchecked, the state for displayEmailOnProfile is true?
Then when I hit submit, it's not updating anything on the mongoose model?
constructor (props) {
super(props);
this.state = {
displayEmailOnProfile: false,
errors: {}
}
this.onSubmit = this.onSubmit.bind(this);
this.onCheck = this.onCheck.bind(this);
}
componentWillReceiveProps(nextProps) {
// Set component fields state
this.setState({
displayEmailOnProfile: profile.displayEmailOnProfile,
});
}
}
onChange = (e) => {
this.setState({[e.target.name]: e.target.value});
}
onSubmit = (e) => {
e.preventDefault();
const profileData = {
displayEmailOnProfile: this.state.displayEmailOnProfile
}
this.props.createProfile(profileData, this.props.history);
}
onCheck = (e) => {
console.log(this.state);
console.log(e.currentTarget.checked);
this.setState({
displayEmailOnProfile: e.currentTarget.checked
});
}
And here is the HTML / React markup
<div className="form-check mb-4">
<input className="form-check-input" type="checkbox" id="defaultCheck1" name="displayEmailOnProfile" checked={this.state.displayEmailOnProfile} onChange={this.onCheck}></input>
<label class="form-check-label" for="customCheck1">Display Email On Profile</label>
</div>
Your code is completely correct and console.log is printing the right thing. You are doing console.log inside the onCheck method — which is printing the current value.
What happens is, when you do set state, life cycle methods are called asynchronously which update the component and set the state. So, if you do console.log of your state right after setting the state (which is synchronous), it will print the previous state, because it is not yet updated.
Following are the lifecycle methods which are called:
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
So, if you want to check if the value was updated or not, you should do console.log inside componentDidUpdate. It will tell you your updated state.
In order to explain the working, I have created a short demo for you on code sandbox with your code sample. Which explains the working and shows that your code is correct.
Now, as far as it is concerned that Mongoose is not updating the values, I can't say anything without looking at your code. You must be having some mistakes in the code for updating the values. I'm not sure, but cross check if you are using the same page for updating as for submit make sure, you change the request from post to put and check for how to update values using Mongoose.
I have a TextField(Material-UI) for input, On the same page have two tables with a lot of values.
To get value entered in textField, I have used onChange event with setState like below.
The issue is where using this method, for every value change, calling setState in order to render the page for every value change. This leads to making slowness in displaying the value on the field when a user is entering values in fast. Below is my code snippet to use for this.
<TextField
onChange={this.onChange}
className={classes.textField}
label={Multilanguage.Translate(this.props.label)}
autoFocus={true}
/>
onChange = (event) => {
if (event.target.value != null) {
this.setState({ serial: event.target.value });
}
}
How to avoid unnecessary rendering when changing in TextField? What is the good way to handle this?
Note my ReactJs version is 16.4.0
Lagging usually happens when React is doing unnecessary work. By every setState your component and all of its children in the DOM get re-rendered. React doesn't update a component if its state or props has not changed, But even checking for whether or not it should re-render is a lot of computation that may lead to slowness.
I'd say add the following lifecycle method to all of your components. It eliminates unnecessary re-renders. The performance boost would be obvious.
shouldComponentUpdate = (nextProps, nextState) => {
return shallowCompare(this, nextProps, nextState);
},
Changing Component to PureComponent is an alternative of the code above.
If you are using hooks, React.memo should do the trick.
Find the expensive components that are actually unnecessarily rendering and wrap them in a React.memo HOC, like so ...
Import React, {memo}
...
const MyExpensiveComponent = (props) => {
...
render()
}
export default memo(MyExpensiveComponent)
If you are subscribing to a context (i.e useContext hook), you may encounter some issues, like I do. I found this post that is worth to read
https://github.com/facebook/react/issues/15156#issuecomment-474590693
or read more about memo https://reactjs.org/docs/react-api.html#reactmemo
I am implementing the following code based on the following page: https://facebook.github.io/react/docs/forms.html
class Reservation extends React.Component {
constructor(props) {
super(props);
this.state = {
isGoing: true,
numberOfGuests: 2
};
this.handleInputChange = this.handleInputChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
handleSubmit(event) {
event.preventDefault();
let data = {
isGoing: this.state.isGoing,
numberOfGuests: this.state.numberofGuests
}
/* Send data in ajax request here */
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Is going:
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange} />
</label>
<br />
<label>
Number of guests:
<input
name="numberOfGuests"
type="number"
value={this.state.numberOfGuests}
onChange={this.handleInputChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
Some questions I have about it:
Why do we need to store the component values in state? Why not
just grab the values we need when the form is submitted as normally
would be done with standard JavaScript? The recommended way would seem to reload the render function for every single character typed in or deleted. To me this makes little sense.
Since the execution of setState is asynchronous and there may be a delay between this.setState() and actually accessing the state with this.state.numberOfGuests does this mean that this code may end up grabbing the state before it has been set? If so, why is this code being suggested in the official React docs? If not, why not?
Regarding point number two, then yes it is logically possible that handleSubmit could run before the state update in handleInputChanged has completed. The reason this isn't mentioned in the React docs, or is generally a concern for anyone is because the setState function runs really quickly. As an experiment I made a codepen to determine the average time taken for setState to run. It seems to take in the order of around 0.02 milliseconds. There is no way someone can change their input, then submit the form in less than that time. In fact, the e.preventDefault() call in handleSubmit takes nearly a quarter of that time anyway.
If you have a situation where it is absolutely crucial that setState has completed before continuing, then you can use a callback function to setState, e.g.
this.setState({
colour: 'red'
}, () => {
console.log(this.state.color)
});
Then red will always be logged, as opposed to the following where the previous value may be logged.
this.setState({
colour: 'red'
});
console.log(this.state.color);
Very good questions! Here's my take on them:
1. Controlled or Uncontrolled - that is the question
You don't have to use controlled form elements. You can use uncontrolled and grab the values as you suggest in your onFormSubmit handler by doing something like event.isGoing.value - plain ole JavaScript (or use refs as some React articles suggest). You can even set a default value with uncontrolled no problem by using, you guessed it, defaultValue={myDefaultValue}.
The above being said, one reason to use controlled components would be if you're looking to give real time feedback while the user is still typing. Say you need to do an autocomplete lookup or provide validation like password strength. Having a controlled component that re-renders with the values in the state makes this super simple.
2. this.setState() asynchronous issues?
[Maybe incorrectly,] I view internally component state updates more like a queue system. No call to this.setState() will be lost and shouldn't overwrite another one when dealing with synchronous code. However, there could be a time where a render is running behind a setState update, but it will eventually have and render the most recent value. Ex: the user types 3 characters, but they only see 2, then a short time later they should see the third. So, there was a point in time where the read to this.state read an "old" value, but it was still eventually updated. I hope I'm making sense here.
Now, I mention synchronous code above because with asynchronous code (like with AJAX) you could potentially introduce a race condition where this.setState() overwrites a newer state value.
Why do we need to store the component values in state?
We don't really need to store the component values in state and it's perfectly fine to access those values on form submit.
However, storing the component values in state has its own advantages. The main reason behind this is to make React state as the single source of truth. When the state is updated (on handleInputChange method), React checks the components (or specifically the parts of components or sub-trees) which needs to be re-rendered.
Using this approach, React helps us to achieve what was earlier achieved via Two Way Binding helpers. In short:
Form updated -> handleInputChange -> state updated -> updated state passed to other components accessing it
For example say, you have a <Card /> component which needs input from the user via a <Form /> component and you wish to display the information as the user types, then the best way would be update your state which would cause React to look for the sub-trees where it was accessed and re-render only those. Thus your <Card /> component will update itself as you type in the <Form />.
Also, React doesn't re-render everything for every single character typed but only those sub-trees which needs to reflect the text change. See the React diffing algorithm for how it is done. In this case, as you type in the characters in a particular field in the form, only those sub-trees in components will be re-rendered which display that field.
Execution of setState and asynchronous behavior
As mentioned by the React docs:
State Updates May Be Asynchronous
React may batch multiple setState() calls into a single update for
performance.
Because this.props and this.state may be updated asynchronously, you
should not rely on their values for calculating the next state.
Your code should work fine since you're not relying on the previous state to calculate the next state and also you're accessing this.state in a different block. So your state updates should be reflected fine; however if you don't want to think about the state updates (or suspect that your code might pick up previous state), React docs also mention an alternative method (which is actually better) that accepts a function rather than an object:
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
If you're still wondering when you can use the setState method safely, use it if other components don't rely on the state or when you don't need to persist the state (save in local storage or a server). When working with larger projects its always a good idea to make use of state containers to save yourself the hassle, like Redux.
This question already has answers here:
Why does calling react setState method not mutate the state immediately?
(9 answers)
The useState set method is not reflecting a change immediately
(15 answers)
Closed 8 months ago.
I would like to ask why my state is not changing when I do an onClick event. I've search a while ago that I need to bind the onClick function in constructor but still the state is not updating.
Here's my code:
import React from 'react';
import Grid from 'react-bootstrap/lib/Grid';
import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col';
import BoardAddModal from 'components/board/BoardAddModal.jsx';
import style from 'styles/boarditem.css';
class BoardAdd extends React.Component {
constructor(props) {
super(props);
this.state = {
boardAddModalShow: false
};
this.openAddBoardModal = this.openAddBoardModal.bind(this);
}
openAddBoardModal() {
this.setState({ boardAddModalShow: true }); // set boardAddModalShow to true
/* After setting a new state it still returns a false value */
console.log(this.state.boardAddModalShow);
}
render() {
return (
<Col lg={3}>
<a href="javascript:;"
className={style.boardItemAdd}
onClick={this.openAddBoardModal}>
<div className={[style.boardItemContainer,
style.boardItemGray].join(' ')}>
Create New Board
</div>
</a>
</Col>
);
}
}
export default BoardAdd
Your state needs some time to mutate, and since console.log(this.state.boardAddModalShow) executes before the state mutates, you get the previous value as output. So you need to write the console in the callback to the setState function
openAddBoardModal() {
this.setState({ boardAddModalShow: true }, function () {
console.log(this.state.boardAddModalShow);
});
}
setState is asynchronous. It means you can’t call it on one line and assume the state has changed on the next.
According to React docs
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value. There is no
guarantee of synchronous operation of calls to setState and calls may
be batched for performance gains.
Why would they make setState async
This is because setState alters the state and causes rerendering. This
can be an expensive operation and making it synchronous might leave
the browser unresponsive.
Thus the setState calls are asynchronous as well as batched for better
UI experience and performance.
Fortunately setState() takes a callback. And this is where we get updated state.
Consider this example.
this.setState({ name: "myname" }, () => {
//callback
console.log(this.state.name) // myname
});
So When callback fires, this.state is the updated state.
You can get mutated/updated data in callback.
For anyone trying to do this with hooks, you need useEffect.
function App() {
const [x, setX] = useState(5)
const [y, setY] = useState(15)
console.log("Element is rendered:", x, y)
// setting y does not trigger the effect
// the second argument is an array of dependencies
useEffect(() => console.log("re-render because x changed:", x), [x])
function handleXClick() {
console.log("x before setting:", x)
setX(10)
console.log("x in *line* after setting:", x)
}
return <>
<div> x is {x}. </div>
<button onClick={handleXClick}> set x to 10</button>
<div> y is {y}. </div>
<button onClick={() => setY(20)}> set y to 20</button>
</>
}
Output:
Element is rendered: 5 15
re-render because x changed: 5
(press x button)
x before setting: 5
x in *line* after setting: 5
Element is rendered: 10 15
re-render because x changed: 10
(press y button)
Element is rendered: 10 20
Live version
Since setSatate is a asynchronous function so you need to console the state as a callback like this.
openAddBoardModal(){
this.setState({ boardAddModalShow: true }, () => {
console.log(this.state.boardAddModalShow)
});
}
setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied. If you need to set the state based on the previous state, read about the updater argument below.
setState() will always lead to a re-render unless shouldComponentUpdate() returns false. If mutable objects are being used and conditional rendering logic cannot be implemented in shouldComponentUpdate(), calling setState() only when the new state differs from the previous state will avoid unnecessary re-renders.
The first argument is an updater function with the signature:
(state, props) => stateChange
state is a reference to the component state at the time the change is being applied. It should not be directly mutated. Instead, changes should be represented by building a new object based on the input from state and props. For instance, suppose we wanted to increment a value in state by props.step:
this.setState((state, props) => {
return {counter: state.counter + props.step};
});
Think of setState() as a request rather than an immediate command to
update the component. For better perceived performance, React may
delay it, and then update several components in a single pass. React
does not guarantee that the state changes are applied immediately.
Check this for more information.
In your case you have sent a request to update the state. It takes time for React to respond. If you try to immediately console.log the state, you will get the old value.
The above solutions don't work for useState hooks.
One can use the below code
setState((prevState) => {
console.log(boardAddModalShow)
// call functions
// fetch state using prevState and update
return { ...prevState, boardAddModalShow: true }
});
This callback is really messy. Just use async await instead:
async openAddBoardModal(){
await this.setState({ boardAddModalShow: true });
console.log(this.state.boardAddModalShow);
}
If you want to track the state is updating or not then the another way of doing the same thing is
_stateUpdated(){
console.log(this.state. boardAddModalShow);
}
openAddBoardModal(){
this.setState(
{boardAddModalShow: true},
this._stateUpdated.bind(this)
);
}
This way you can call the method "_stateUpdated" every time you try to update the state for debugging.
Although there are many good answers, if someone lands on this page searching for alternative to useState for implementing UI components like Navigation drawers which should be opened or closed based on user input, this answer would be helpful.
Though useState seems handy approach, the state is not set immediately and thus, your website or app looks laggy... And if your page is large enough, react is going to take long time to compute what all should be updated upon state change...
My suggestion is to use refs and directly manipulate the DOM when you want UI to change immediately in response to user action.
Using state for this purspose is really a bad idea in case of react.
setState() is asynchronous. The best way to verify if the state is updating would be in the componentDidUpdate() and not to put a console.log(this.state.boardAddModalShow) after this.setState({ boardAddModalShow: true }) .
according to React Docs
Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately
According to React Docs
React does not guarantee that the state changes are applied immediately.
This makes reading this.state right after calling setState() a potential pitfall and can potentially return the existing value due to async nature .
Instead, use componentDidUpdate or a setState callback that is executed right after setState operation is successful.Generally we recommend using componentDidUpdate() for such logic instead.
Example:
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
constructor() {
super();
this.state = {
counter: 1
};
}
componentDidUpdate() {
console.log("componentDidUpdate fired");
console.log("STATE", this.state);
}
updateState = () => {
this.setState(
(state, props) => {
return { counter: state.counter + 1 };
});
};
render() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button onClick={this.updateState}>Update State</button>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
this.setState({
isMonthFee: !this.state.isMonthFee,
}, () => {
console.log(this.state.isMonthFee);
})
when i was running the code and checking my output at console it showing the that it is undefined.
After i search around and find something that worked for me.
componentDidUpdate(){}
I added this method in my code after constructor().
check out the life cycle of react native workflow.
https://images.app.goo.gl/BVRAi4ea2P4LchqJ8
Yes because setState is an asynchronous function. The best way to set state right after you write set state is by using Object.assign like this:
For eg you want to set a property isValid to true, do it like this
Object.assign(this.state, { isValid: true })
You can access updated state just after writing this line.