I'm trying to update state and set preview value to undefined, but it's not working.
here is my code
console.log(preview);
this.setState({ preview }, () => {
console.log(this.state.preview);
});
I try to put something else in preview, but still it's not working.
undefined
Object { ... }
other value in state is working, just this one it's not working.
UPDATE
another weird thing is that when I update another value it will be updated.
for example
this.setState({preview:undefined,media:undefined});
the media will updated to undefined, but preview will not.
React state updates are asynchronous and batched processed for the next render/commit cycle. What this means then is if any other part of the code also is setting the same state to some value after an update, that update is lost.
Demo
class App extends Component {
state = {
value: "test"
};
updateState = () => {
this.setState({ value: undefined }, () =>
console.log("value", this.state.value) // logs "test2"!!
);
this.setState({ value: "test2" });
};
render() {
return (
<div>
<div>state: {this.state.value}</div>
<button type="button" onClick={this.updateState}>
Update state
</button>
</div>
);
}
}
Related
I'm very new to react and I got two problems:
I want to console log the input and display the mapped data after clicking the submit button once. But I get console logged the input and the mapped data after clicking the button twice.
I wanna clear the mapped list (data from previous input) and display new list items depending on the input. But the new list items are only added to the end of the previous list (only the last list item from the previous list got overwritten by the first list item of the new list).
So this is the code from my app component:
import React, { Component, Fragment } from 'react';
import './App.css';
import Display from './/Display';
class App extends Component {
constructor(props) {
super(props);
this.state = {
value: "",
passedValue: ""
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ value: event.target.value });
}
handleSubmit(event) {
this.setState({ passedValue: this.state.value });
console.log(this.state.passedValue);
event.preventDefault();
}
render() {
return (
<div>
<form className="inputContainer" onSubmit={this.handleSubmit}>
<input type="text" name="company_name" onChange={this.handleChange} />
<input type="submit" value="Submit" />
</form>
<Display listDataFromParent={this.state.passedValue} />
</div>
);
}
}
export default App;
And this is my display component:
import React, { Component } from 'react'
import "./Display.css";
export default class Display extends Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
data: []
};
}
componentWillReceiveProps() {
fetch("http://localhost:5000/company?company_name=" + this.props.listDataFromParent)
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
data: result
});
},
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components.
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
render() {
const { error, isLoaded, data } = this.state;
// if (error) {
// return <div>Error: {error.message}</div>;
// } else if (!isLoaded) {
// return <div>Loading...</div>;
// } else {
return (
<div className="display">
<h1>Kreditnehmer</h1>
<ul>
{this.props.listDataFromParent}
{data.map(item => (
<li key={item.c.company_id}>
Relation type: {item.r.relation_group}
Last name: {item.p.last_name}
</li>
))}
</ul>
</div>
);
}
}
Can anyone help?
1) setState is async method in react means it will take some time to update the component state. You can get your console log by using callback function of setState like
this.setstate({ value: e.target.value }, () => { console.log(this.state.value) });
2) in display component, your using componentWillReciveProps life cycle and inside that your using this.props.listdatafromparent which is pointing previous props. Rather than using this.props I would suggest consider props param of life cycle, means it should be like
componentWillReciveProps(props) {
// your code
Console.log(props.listdatafromparent);
}
The handleSubmit method is wrong... the console log is executed before the state is changed. You need to put it inside a callback function as a second parameter of setState.
this.setState({ passedValue: this.state.value }, () => {
console.log(this.state.passedValue);
});
Answers are:
1) Callback function should be used on setState, in order to do console.log after state is really updated.
In your case you call setState and setState is async function, which means that console.log won't wait until state is really updated.
Your code should be:
handleSubmit(event) {
this.setState({ passedValue: this.state.value },
() => console.log(this.state.passedValue));
event.preventDefault();
}
2) I would move data fetching out of componentWillReceiveProps(), since this lifecycle method will be deprecated from version 17 and it is fired on every render(). Try replacing with componentDidMount() or componentDidUpdate(). Maybe just this small change will solve your problem. If not pls post results and I will take a look again.
I have a Parent component:
import React, { Component } from "react";
import { Button } from "./Button";
export class Dashboard extends Component {
constructor(props) {
super(props);
this.state = {
numbers: [],
disabled: false
};
this.setNum = this.setNum.bind(this);
}
setNum(num) {
if (!this.state.numbers.includes(num)) {
this.setState(prevState => ({
numbers: [...prevState.numbers, num]
}));
} else if (this.state.numbers.includes(num)) {
let nums = [...this.state.numbers];
let index = nums.indexOf(num);
nums.splice(index, 1);
this.setState({ numbers: nums });
console.log(this.state.numbers);
}
if (this.state.numbers.length >= 4) {
this.setState({ disabled: true });
} else if (this.state.numbers.length < 4) {
this.setState({ disabled: false });
}
}
render() {
return (
<div className="board-container">
<div className="board">
<div className="row">
<Button
id="1"
numbers={this.state.numbers}
onChange={this.setNum}
disabled={this.state.disabled}
/>
<Button
id="2"
numbers={this.state.numbers}
onChange={this.setNum}
disabled={this.state.disabled}
/>
<Button
id="3"
numbers={this.state.numbers}
onChange={this.setNum}
disabled={this.state.disabled}
/>
<Button
id="4"
numbers={this.state.numbers}
onChange={this.setNum}
disabled={this.state.disabled}
/>
</div>
</div>
</div>
);
}
}
... and a Child component:
import React, { Component } from "react";
export class Button extends Component {
constructor(props) {
super(props);
this.state = {
isChecked: false
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({
isChecked: !this.state.isChecked
});
var num = e.target.value;
this.props.onChange(num);
}
render() {
const { isChecked } = this.state;
if (isChecked === true) {
var bgColor = "#f2355b";
} else {
bgColor = "#f7f7f7";
}
let disabled = this.props.disabled;
if (this.props.numbers.includes(this.props.id)) {
disabled = false;
}
return (
<div className="number-container" id="checkboxes">
<label
className={!isChecked && disabled === false ? "num" : "checked-num"}
style={{ backgroundColor: bgColor }}
>
{" "}
{this.props.id}
<input
type="checkbox"
name={this.props.id}
value={this.props.id}
id={this.props.id}
onChange={this.handleChange}
checked={isChecked}
disabled={disabled}
/>
</label>
</div>
);
}
}
Whenever any Button component is clicked, the Parent component gets the child Button's id value and puts it into its numbers state array. Whenever a Button is unchecked, the Parent updates is numbers state by removing the id of the child Button.
If my code is right, the expected behavior is whenever a Button checkbox is clicked, the Parent numbers state will be updated immediately (adding or removing a number). However, it always updates with one step lag behind.
I know, that the issue is dealing with the React states not being updated instantly, and I've checked similar issues on Stackoverflow. The problem is that I can't figure it out how to make this two components interact with each other in a proper way. What would be the solution for this issue?
Here are three screenshots from codesandbox
If you want to play with it please find the link https://codesandbox.io/s/w2q8ypnxjw
What I did was, I basically copied and pasted your code and updated setNum function to reflect the changes Think-Twice suggested
setNum(num) {
if (!this.state.numbers.includes(num)) {
this.setState(
prevState => ({
numbers: [...prevState.numbers, num]
}),
() => {
console.log("state logged inside if", this.state.numbers);
}
);
} else if (this.state.numbers.includes(num)) {
let nums = [...this.state.numbers];
let index = nums.indexOf(num);
nums.splice(index, 1);
this.setState({ numbers: nums }, () => {
console.log("state logged inside else if", this.state.numbers);
});
}
if (this.state.numbers.length >= 4) {
this.setState({ disabled: true });
} else if (this.state.numbers.length < 4) {
this.setState({ disabled: false });
}
}
So before going further let's quickly address a couple of things regarding to React and setState
As B12Toaster mentioned and provided a link which contains a
quote from official documentation
setState() does not always immediately update the component. It may
batch or defer the update until later.
Think-Twice's also points out that by stating
Basically setState is asynchronous in React. When you modify a value
using setState you will be able to see the updated value only in
render..
So if you want to see the immediate state change in a place which
you trigger setState, you can make use of a call back function as
such setState(updater[, callback])
There are two approaches when it comes to and updater with setState,
you could either pass an object, or you could pass a function So in
Think-Twice's example, an object is passed as an updater
this.setState({ numbers: nums } //updater, () => {
console.log(this.state.numbers); //this will print the updated value here
});
When a function is used as an updater (in your setNum function you
already do that), the callback function can be utilized like below
if (!this.state.numbers.includes(num)) {
this.setState(
prevState => ({
numbers: [...prevState.numbers, num]
}),
() => {
console.log("state logged inside if", this.state.numbers);
}
);
}
Your current implementation and communication structure seems fine. It is actually called Lifting State Up which is recommended also by official documentation.
Basically you store the state of array numbers in a parent component (which can be considered as the source of truth) and you pass the method that changes the state as a prop to it's child component.
In the codesandbox link I provided, the functionalities works the way I expect (at least this is what I expect from your code)
Basically setState is asynchronous in React. When you modify a value using setState you will be able to see the updated value only in render. But to see updated state value immediately you need to do something like below
this.setState({ numbers: nums }, () => {
console.log(this.state.numbers); //this will print the updated value here
});
i'm trying to learn how the image upload process works with react. i have an input that takes image data in and a handler that sets that information to a variable but i am doing something incorrect and not sure what it is.
I want to update picture in state with the picture information and leave the other items in state alone. When i do the below, and console.log after the state of picture stays null.
class Profile extends Component {
constructor() {
super();
this.state = {
userName: "",
userEmail: "",
picture: null
};
this.handleNewImage = this.handleNewImage.bind(this);
}
handleNewImage = event => {
this.setState({
picture: event.target.files[0]
})
console.log(this.state.picture); //gives null still
}
render() {
return (
<input type='file' onChange={this.handleNewImage} />
);
}
}
This probably has nothing to do with images but rather an understanding of setState. React's setState is an async operation so you'd need to console log once the state update is complete:
handleNewImage = event => {
this.setState({
picture: event.target.files[0]
}, () => {
console.log(this.state.picture);
});
}
More info here:
https://reactjs.org/docs/react-component.html#setstate
https://css-tricks.com/understanding-react-setstate/
I Have a ReactJS checkbox component. When onChange is called I can log the new state and see it changing, but it never actually re-renders the checkbox into the new state. So the ADD_ID action is never called. See code below:
class CheckBox extends React.Component {
constructor(props) {
super(props)
this.state = {
checked: true
}
}
changing = (e) => {
this.setState(prevState => ({checked: !prevState.checked}), () => {
console.log(this.state.checked); // false
this.state.checked
? store.dispatch({ type: 'ADD_ID', id: this.props.id })
: store.dispatch({ type: 'REMOVE_ID', id: this.props.id });
});
}
render() {
return (
<label>
Include
<input onChange={this.changing} checked={this.state.checked} type='checkbox'/>
</label>
)
}
}
Is there a lifecycle hook that I have to call? I was under the impression that the component would re-render when either it's props or state changes, in this case, as shown by the console.log(this.state.checked), the state has changed, but the component doesn't re-render.
The event has already a checked property for you. You're doing it in a way that's a bit weird.
Change your function to something like:
handleChange = (e) => {
const isChecked = e.target.checked
if(isChecked){
store.dispatch({type:'ADD_ID', id:this.props.id})
} else {
store.dispatch({type:'REMOVE_ID', id:this.props.id})
}
this.setState(checked: isChecked)
}
It's however still strange that you're using at the same time internal state and Redux. You may want to rethink your approach here.
React docs on forms, which I recommend you to read in 5 min:
https://reactjs.org/docs/forms.html
I was reading the documentation about React setState, which says:
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.
Now I have a component like this:
class NoteScreenComponent extends React.Component {
constructor() {
super();
this.state = { note: Note.newNote() }
}
componentWillMount() {
this.setState({ note: this.props.note });
}
noteComponent_change = (propName, propValue) => {
this.setState((prevState, props) => {
let note = Object.assign({}, prevState.note);
note[propName] = propValue;
return { note: note }
});
}
title_changeText = (text) => {
this.noteComponent_change('title', text);
}
body_changeText = (text) => {
this.noteComponent_change('body', text);
}
saveNoteButton_press = () => {
// Save note to SQL database
Note.save(this.state.note)
}
render() {
return (
<View>
<TextInput value={this.state.note.title} onChangeText={this.title_changeText} />
<TextInput value={this.state.note.body} onChangeText={this.body_changeText} />
<Button title="Save note" onPress={this.saveNoteButton_press} />
</View>
);
}
}
What I'm wondering is, since setState does not update the state immediately, how can I know if the note I'm saving in saveNoteButton_press is the current version of the state? Is there some callback or something that I could poll to know if state has been fully updated?
What they are warning against is trying to do something in the same event loop.
method = () => {
this.setState({ note: 'A' })
saveNote(this.state.note) // <-- this.state.note will not have been updated yet.
}
or to setState using previous state:
method = () => {
let note = this.state.note // possible that `this.state.note` is scheduled to change
this.setState({ note: note + 'B' })
}
Since your user is going to be pushing the button after the setState scheduling, the state will have already been updated.
..but for theory's sake, let's imagine that somehow the input event and button happen in the exact same moment.. what would be the correct solution? If it was a single function call you probably wouldn't be using the new state since you already have the new note and the previous state.
method = (text) => {
let noteToSave = this.state.note + text // old state + new value
saveNote(noteToSave) // maybe this will fail
.then(response => this.setState({ note: noteToSave }))
.catch(err => this.setState({ error: 'something went wrong' }))
// optimistically update the ui
this.setState({ note: noteToSave })
}
but probably the most likely solution is to just pass what you want as an argument where you use it, rather than trying to access state which might be in a race condition, since render will happen after any state transitions.
method = (note) => {
noteToSave(note)
}
render() {
return (
<Button onPress={() => this.method(this.state.note)} /> <-- must be up to date here
)
}