How to update component state that is connected by componentDidUpdate() - reactjs

I am passing selectedOrderState as props from parent and want to populate the state and that works but can't figure how to change the state for use in an input field with an onChange=(handleChange) function attached to manipulate the data. Seems as though componentDidUpdate() and getDerivedStateFromProps() both seem to lock the state so no change can occur. **componentDidMount also does not work because the selectedOrderState prop comes from an onClick event and so the component had already mounted.
Code below - Any thoughts would be helpful!
import React, { Component } from 'react'
export class addOrder extends Component {
state = {
AoOrder: false,
AoProgress: false,
AoChat: false,
visibility: "visible",
Order: {},
DeliveryDate:"",
};
//Functs
componentDidUpdate(prevProps){
if(this.props.selectedOrderState !== this.state.Order){
this.setState({
Order:this.props.selectedOrderState
});
}
}
handleChange = (e) => {
this.setState({
Order:{
...this.state.Order,
[e.target.id]: e.target.value,
}
})
};
handleSubmit = () => {
};
};
render() {
const order = this.props.selectedOrderState;
const { user: { credentials: { handle, imageUrl}}} = this.props;
return (
<form className='OrderInfo'onSubmit={this.handleSubmit}>
<div className='OrderInfoLbl'>Order Id:</div>
<div className="OrderInfoInput">{this.props.selectedOrderState.OrderId}</div>
<div className='OrderInfoLbl'>Delivery Date:</div>
<input className="OrderInfoInput" id="DeliveryDate" type="text" onChange=
{this.handleChange}></input>
<img className="ProfileBioSubmit" onClick={this.handleSubmit}
src="./images/svg/AcceptBtns.svg" alt="Edit"></img>
</form>
)
}
}
export default addOrder

Declare your state inside the constractor and bind your functions. I'm inviting you to take a look to forms docs with react
constructor(props) {
super(props);
this.state = {
AoOrder: false,
AoProgress: false,
AoChat: false,
visibility: "visible",
Order: {},
DeliveryDate:"",
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}

Probably the most hackerish way to do something but it worked:)
What i did was keep the componentDidUpdate() feeding the state to the child component but from the parent i passed down a function called handleChangeUP() for which i was able to use pass the event of onChange data through to change the original state selectedOrderState. Have a look!
Child
import React, { Component } from 'react'
export class addOrder extends Component {
state = {
AoOrder: false,
AoProgress: false,
AoChat: false,
visibility: "visible",
Order: {},
};
//Functs
componentDidUpdate(prevProps){
if(this.props.selectedOrderState !== this.state.Order){
this.setState({
Order:this.props.selectedOrderState
});
}
}
handleChange = (e) => {
this.props.handleChangeUP(e)
};
render() {
const order = this.props.selectedOrderState;
const { user: { credentials: { handle, imageUrl}}} =
this.props;
return (
<form className='OrderInfo'onSubmit={this.handleSubmit}>
<div className='OrderInfoLbl'>Order Id:</div>
<div className="OrderInfoInput">
{this.props.selectedOrderState.OrderId}</div>
<div className='OrderInfoLbl'>Delivery Date:</div>
<input className="OrderInfoInput" id="DeliveryDate" type="text"
value={this.state.Order.DeliveryDate}
onChange={this.handleChange}></input>
<img className="ProfileBioSubmit" onClick={this.handleSubmit}
src="./images/svg/AcceptBtns.svg" alt="Edit"></img>
</form>
)
}
}
export default addOrder
Parent
import React, { Component } from 'react'
import Child from './child'
export class Parent extends Component {
state = {
creating: false,//creat order window toggle
profiling: false,//Profile window toggle
chatting: false,//Chat window toggle
searching: false,//Search inside Chat window
selectedOrder: {}
};
handleChangeUP = (e) => {
console.log(e.target.id);
this.setState({
// [e.target.id]: e.target.value
//Order: e.target.value
selectedOrder:{
...this.state.selectedOrder,
[e.target.id]: e.target.value
}
})
}
render() {
return (
<div className="Wrapper">
<Child handleChangeUP={this.handleChangeUP}
selectedOrderState={this.state.selectedOrder}/>
</div>
)
}
}
export default Parent;

Related

Why is this.state not updated real-time after setState is issued?

So I'm trying to simulate the state by clicking a button. The 'before' status seems to have the correct value, but why is the 'after' not displaying the correct value even if the setState is already hit by the code?
class App extends Component {
constructor(){
super()
this.state = {isLoggedIn: false}
this.OnClick = this.OnClick.bind(this);
}
OnClick(){
this.setState(prev =>
{
return (prev.isLoggedIn = !this.state.isLoggedIn);
})
console.log(`After setState value: ${this.state.isLoggedInstrong text}`) // setState is done, why is this.state displaying incorrect value?
}
render()
{
console.log(`Before setState value: ${this.state.isLoggedIn}`)
return <Login isLoggedIn={this.state.isLoggedIn} OnClick={this.OnClick} />
}
}
import React from "react";
class Login extends React.Component
{
render()
{
const {isLoggedIn, OnClick} = this.props;
return (
<div>
<button onClick={OnClick} >{isLoggedIn ? "Log Out" : "Log In"} </button>
</div>
)
}
}
export default Login;
OUTPUT:
"Before setState value: false"
(Initial display, button value is: Log In)
When button is clicked:
"After setState value: false" <------ why false when setState has been hit already? Not real-time update until Render is called?
"Before setState value: true"
(Button value is now: Log Out)
The main problem I see in your code is you’re trying to mutate the state.
this.setState(prev => {
return (prev.isLoggedIn = !this.state.isLoggedIn);
})
You have to merge to the state not mutate it. You can do it simply by returning an object like this.
this.setState((prev) => {
return { isLoggedIn: !prev.isLoggedIn };
});
This will fix all the weird behaviours in your code.
Full Code
App.js
import { Component } from "react";
import Login from "./Login";
class App extends Component {
constructor() {
super();
this.state = { isLoggedIn: false };
this.OnClick = this.OnClick.bind(this);
}
OnClick() {
this.setState((prev) => {
return { isLoggedIn: !prev.isLoggedIn };
});
console.log(`After setState value: ${this.state.isLoggedIn}`);
}
render() {
console.log(`Before setState value: ${this.state.isLoggedIn}`);
return <Login isLoggedIn={this.state.isLoggedIn} OnClick={this.OnClick} />;
}
}
export default App;
Login.js
import { Component } from "react";
class Login extends Component {
render() {
const { isLoggedIn, OnClick } = this.props;
return (
<div>
<button onClick={OnClick}>{isLoggedIn ? "Log Out" : "Log In"} </button>
</div>
);
}
}
export default Login;
CodeSandbox - https://codesandbox.io/s/setstate-is-not-update-the-state-69141369-efw46
try this
this.setState({
isLoggedIn:!this.state.isLoggedIn
})
or
this.setState(prev => ({
isLoggedIn:!prev.isLoggedIn
}))

calling function in React SetState gives error that userName is unlabelled why?

import React,{Component} from 'react'
class Formhandler extends Component {
constructor(props) {
super(props)
this.state = {
userName:""
}
}
changer=(event)=>{
this.setState(()=>{
userName : event.target.value
})
}
render()
{
return(
<div>
<label>UserName</label>
<input type="text" value={this.state.userName} onChange={this.changer}/>
</div>
)
}
}
export default Formhandler
You are getting the error because of invalid syntax.
Update changer function
changer = (event) => {
this.setState({ userName: event.target.value });
};
You need to return an object inside the setState function but you are not that's the source of issue(syntax error).
use a function inside setState when your new state value would depend on your previous state value, where the function passed inside the setState will receive previous state as argument
changer = (e) => {
this.setState((prevState) => ({
userName : e.target.value
})
);
}
pass an object to update the state, use this when it doesn't depend on your previous state value.
changer = (e) => {
this.setState({ userName: e.target.value });
};
import React from "react";
class Formhandler extends React.Component {
constructor(props) {
super(props);
this.state = {
userName: "",
};
}
changer(event) {
this.setState(() => ({
userName: event.target.value,
}));
}
render() {
return (
<div>
<label>UserName</label>
<input
type="text"
value={this.state.userName}
onChange={this.changer.bind(this)}
/>
</div>
);
}
}
export default Formhandler;
It will work, compare your version and this

Input doesn't change color according to the validation (rcc)

I want to validate the value that the user write in the input.
The browser works, creating a new room with the click of a button works, but the input doesn't change color according to the validation I set, why?
Inside addRoomName function I created setState for the value inside the room input
addRoomName=(e)=> {
this.setState({ room: e.target.value })
and additionally I created setState for the validation with the conditions
this.setState({ addRoomName: e.target.value });
if (e.target.value.length >= 6){
this.setState({roomNameInputColor:'green'})
} else {
this.setState({roomNameInputColor:'red'})
}
Is that may be the problem? because it seems that the react don't even recognize the validation but just the first setState (the one that bring the value that wrote in the room input)
So why the input doesn't change color?
I shared all the code
thanks!
App.js
import React, { Component } from 'react'
import './App.css';
import Addroom from './components/Addroom.js'
import Room from './components/Room.js'
export default class App extends Component {
state = {
roomsList:[{room:'',color:''}],
}
create = (r, c) => {
this.setState({ roomsList: [...this.state.roomsList, { room: r, color: c }] })
}
render() {
return (
<div>
<h1>My Smart House</h1>
{this.state.roomsList.map((element) => {
return <Room r={element.room} c={element.color} />
})}
<Addroom add={this.create}/>
</div>
)
}
}
Addroom.js
import React, { Component } from 'react'
export default class Addroom extends Component {
constructor(props) {
super(props)
this.state = {
roomNameInputColor:'white',
}
}
addRoomName = (e) => {
this.setState({ room: e.target.value })
this.setState({ addRoomName: e.target.value });
if (e.target.value.length >= 6) {
this.setState({ roomNameInputColor: 'green' })
} else {
this.setState({ roomNameInputColor: 'red' })
}
}
addColor = (e) => {
this.setState({ color: e.target.value })
}
createRoom = () => {
this.props.add(this.state.room, this.state.color);
}
render () {
return (
<div>
<input onChange={this.addRoomName} style={{ backgroundInputColor: this.state.roomNameInputColor }} placeholder='Name Your Room'/>
<br/>
<input onChange={this.addColor} placeholder='Whats The Room Color?'/>
<br/>
<button onClick={this.createRoom}>Create</button>
</div>
)
}
}
Room.js
import React, { Component } from 'react'
export default class Room extends Component {
constructor(props) {
super(props)
this.state = {}
}
render() {
return (
<div>
<h1>Room: {this.props.r} </h1>
<h3>Color: {this.props.c} </h3>
</div>
)
}
}
In your addRoomName function, you are doing multiple setState in a row, where it's often a source of state confusions (that you are probably experiencing here).
Prefer to have a single call to the setState() method in your function like this:
addRoomName = (e) => {
const room = e.target.value;
let roomNameInputColor = '';
if (room.length >= 6) {
roomNameInputColor = 'green';
} else {
roomNameInputColor = 'red';
}
this.setState({ room, addRoomName: room, roomNameInputColor });
}
thanks everyone, now it works, I did like you send guys to have effective code and also I changed this
<input onChange={this.addRoomName} style={{backgroundInputColor:this.state.roomNameInputColor}} placeholder='Name Your Room'/><br/>
To this
<input onChange={this.addRoomName} style={{backgroundColor:this.state.roomNameInputColor}} placeholder='Name Your Room'/><br/>
Because backgroundColor is a reserved word and while I tried to fix the problem I didn't saw that little important thing.. thanks!

Reset react-select dropdown when new props passed in

I would like to clear the value in the dropdown on getting new props. Currently, the previous value remains visible even though if I expand the dropdown, I can see new values.
import React from 'react';
import Select from 'react-select';
import axios from 'axios';
class KeydateDropdown extends React.Component {
constructor(props) {
super(props);
this.state = {
optionList: []
};
};
componentDidUpdate(prevProps) {
let vesselname = this.props.vesselname;
if (prevProps.vesselname !== vesselname) {
let keydateList = [];
this.setState({
optionList: keydateList
});
axios.get('list-keydates', {
params: {
vesselname: vesselname
}
})
.then((response) => {
let data = response.data['intervention'];
data.forEach((element) => {
keydateList.push({ value: element, label: element });
});
this.setState({ optionList: keydateList });
})
}
}
render() {
return (
<Select
isDisabled={this.props.isDisabled}
onChange={this.props.handleKeydateChange}
options={this.state.optionList}
className={styles.dropdown}
/>
);
}
}
export default KeydateDropdown;
I have confirmed that the first setState() inside componentDidUpdate which clears optionList calls render()
Edit: Here's the codesandbox
You forget to explicitly set a value on the Select component. You have to pass this.state.selectedKeydate to KeydateDropdown in MainInputBar and then pass that value to the Select component:
In MainInputBar.jsx:
keydateDropdown = (
<KeydateDropdown
vesselname={this.state.selectedVessel.value}
selectedKeydate={this.state.selectedKeydate}
handleKeydateChange={this.handleKeydateChange}
value={this.state.selectedKeydate}
/>
);
In KeyStateDropdown.jsx:
<Select
isDisabled={this.props.isDisabled}
onChange={this.props.handleKeydateChange}
options={this.state.optionList}
className={styles.dropdown}
value={this.props.value}
/>
I forked your sandbox and fixed it here: https://codesandbox.io/s/happy-einstein-55klv

How to update state inside componentDidMount?

I'm using fetch API and I want update the const called state inside the componentDidMount() (with onChange) which are being using in a template string. How do I update this value with onChange?
import React, {Component} from 'react'
class Data extends Component {
constructor() {
super();
this.state = {
items: {},
value: '',
isLoaded: false
}
}
handleChange(e) {
this.setState({value: e.target.value});
}
componentDidMount() {
const state = this.state.value
fetch(`http://api.timezonedb.com/v2.1/get-time-zone?key=J9X3EOT2EM8U&format=json&by=zone&zone=${state}`)
.then(res => res.json())
.then(json => {
this.setState({
isLoaded: true,
items: json,
})
});
}
render(){
const {isLoaded} = this.state;
if(!isLoaded) {
return <div>Loading...</div>
}
return(
<div>
<select onChange={this.handleChange}>
<option value="America/Chicago">Chicago</option>
<option value="America/Sao_Paulo">São Paulo</option>
</select>
</div>
)
}
}
So, how can I update the value of the const state with onChange?
componentDidMount() is called when the React component has mounted, and it happens only once.
If I understand correctly, you want to call fetch on each change of the value stored under value state property, so the componentDidMount method is not a perfect place to put that kind of logic. You can create a separate method called fetchData and pass the value to it as an argument. Then you can call that method on componentDidMount as well as on each value property change (in our case - onChange event).
import React, { Component } from "react";
class Data extends Component {
constructor(props) {
super(props);
this.state = {
items: {},
value: "America/Chicago",
isLoaded: false
};
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
const { value } = this.state;
this.fetchData(value);
}
handleChange(event) {
const value = event.target.value;
this.setState({
value
});
this.fetchData(value);
}
render() {
const { isLoaded, value, items } = this.state;
if (!isLoaded) {
return <div>Loading...</div>;
}
return (
<div>
<select onChange={this.handleChange} value={value}>
<option value="America/Chicago">Chicago</option>
<option value="America/Sao_Paulo">São Paulo</option>
</select>
{JSON.stringify(items)}
</div>
);
}
fetchData(value) {
fetch(
`https://api.timezonedb.com/v2.1/get-time-zone?key=J9X3EOT2EM8U&format=json&by=zone&zone=${value}`
)
.then(res => res.json())
.then(json => {
this.setState({
isLoaded: true,
items: json
});
});
}
}
Working demo: https://codesandbox.io/embed/728jnjprmq
Assuming you want to refresh the value of this.state.items when the user changes the value of the select, you can do this in the onChange. However, your code is in a few (incorrect) pieces. Let's start from the top.
First of all, you're setting the value property of state to '', so your componentDidMount function is going to see that value. I assume that's no good, so let's strip that out of componentDidMount entirely. We can move this code to the handleChange function instead, but it'll still need to be changed:
handleChange(e) {
this.setState({value: e.target.value});
fetch(`http://api.timezonedb.com/v2.1/get-time-zone?key=J9X3EOT2EM8U&format=json&by=zone&zone=${e.target.value}`)
.then(res => res.json())
.then(json => {
this.setState({
isLoaded: true,
items: json,
})
});
}
Notice my change - we can't access the value from the state, because setState is asynchronous, and so the value hasn't been updated by this point. We know the value comes from the select though.
The other thing you could do to improve this functionality is to turn the select into a controlled component. To do this, you just have to set the value of the field to be controlled by the state of this component. Since you're using an onChange listener for this, it makes the field a controlled component (if you weren't using an onChange, it would be a read-only field.
The loading variable in state appears to be being used incorrectly, I'm guessing you just need to check if there's data in 'items'. I'll remove this for now, but you could come back to this.
render(){
const {isLoaded} = this.state;
if(!isLoaded) {
return <div>Loading...</div>
}
return(
<div>
<select onChange={this.handleChange}>
<option value="America/Chicago">Chicago</option>
<option value="America/Sao_Paulo">São Paulo</option>
</select>
</div>
)
}
Tomasz's code has 2 mistakes: (1) it fetches resources w/o checking if the component has been unmounted; (2) it starts the request w/o updating the UI first.
I would do the following instead:
import React, {Component} from 'react'
class Data extends Component {
constructor() {
super();
this.state = {
items: {},
value: '',
isLoaded: false
}
this._isMounted = false;
// don't forget to bind your methods
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
this._isMounted = true;
}
componentWillUnmount() {
this._isMounted = false;
}
handleChange(e) {
const value = e.target.value;
this.setState({ value }, () => {
if (!this._isMounted) return;
const url = `http://api.timezonedb.com/v2.1/get-time-zone?key=J9X3EOT2EM8U&format=json&by=zone&zone=${value}`
fetch(url).then((res) => {
if (!this._isMounted) return;
const data = res.json();
this.setState({ isLoaded: true, items: data });
})
});
}
render(){
const { isLoaded } = this.state;
if(!isLoaded) {
return <div>Loading...</div>
}
return(
<div>
<select onChange={this.handleChange}>
<option value="America/Chicago">Chicago</option>
<option value="America/Sao_Paulo">São Paulo</option>
</select>
</div>
)
}
}

Resources