React : state object property is not updating - javascript

I am trying to update my my state but in meantime when I console updated state value in console it give me a message that your state value is undefined . could someon eplease help me how to solve this problem . I am using rc-time-picker for picking a time and storing a selected time in localstorage but it did not updating state value
Thanks
Code
import 'rc-time-picker/assets/index.css';
import React, { Component } from 'react';
import TimePicker from 'rc-time-picker';
import moment from 'moment';
const format = 'h:mm a';
const now = moment().hour(0).minute(0);
class Time extends Component {
constructor(){
super();
this.state={
value : ''
}
}
newHandleChange=(value)=>{
this.setState({
value: localStorage.setItem("currentTime",value.format('h:mm a'))
})
}
render(){
console.log("state data",this.state.value)
return(
<div>
<TimePicker
showSecond={false}
defaultValue={now}
className="xxx"
onChange={this.newHandleChange}
format={format}
use12Hours
inputReadOnly
fieldName="time"
/>
</div>
)
}
}
export default Time;

The reason why you're getting undefined is because you're setting new state with the value that gets returned from calling localStorage.setItem, which is undefined.
Here's what you need to do instead:
class Time extends Component {
constructor(props) {
super(props)
this.state = { value: '' }
}
// ...
newHandleChange = (value) => {
const time = value.format('h:mm a')
localStorage.setItem('currentTime', time)
this.setState({
value: time
})
}
}
This way, you're saving your new time into a variable and using it to update currentValue in localStorage and state in your component.

When initializing state in constructor you should pass props to super:
constructor(props){
super(props);
this.state={
value : ''
}
}
The undefined you're getting is just the result from getItem()

You need to set the proper value to the state, please check below code
newHandleChange=(value)=>{
localStorage.setItem("currentTime",value.format('h:mm a')) // if you really needs the local storage.
this.setState({
value: value.format('h:mm a')
})
}

try to used this `
newHandleChange =val=> {
const value=localStorage.setItem('currentTime', val.format('h:mm a'))
this.setState({
value
})
}`
look to this this app https://codesandbox.io/s/competent-bohr-6jon1

Related

Component not rerendering on state change?

Whenever setState() is called, the component doesn't seem to rerender. As you can see by my comments, the state does in fact change and render seems to be called again, but if I don't add that if statement and simply add a paragraph tag that displays the data it will give me an error. I'm sure I'm missing something simple, but any help is appreciated.
import React from "react";
import axios from "axios";
import { constants } from "../constants/constants";
const { baseURL, apiKey, userName } = constants;
class User extends React.Component {
constructor(props) {
super(props);
this.state = {
user: []
};
}
componentDidMount() {
let getUserInfo = axios.create({
baseURL,
url: `?
method=user.getinfo&user=${userName}&api_key=${apiKey}&format=json`
});
getUserInfo().then(response => {
let data = response.data;
console.log(data.user.playcount); //logs second, displays correct
this.setState(state => ({
user: data
}));
});
}
render() {
console.log(this.state); //logs first and third, doesn't work on first but does on third
let toReturn;
if (this.state.user.length > 0) {
toReturn = <p>{this.state.user.user.playcount}</p>;
} else {
toReturn = <p>didn't work</p>;
}
return <div>{toReturn}</div>;
}
}
export default User;
React LifeCycle function sequence is Constructor and then it calls render method.
In constructor method it initialises the state which is currently empty user array.
Now it calls render() method as this.state.user is an empty array, referencing something out of it gives an error
this.state.user.user.playcount
this will generate an error if you dont have if condition.
After the first render it will call componentDidMount, now you fetch something update state. As setState occurred, render will be called again Now you have something in this.state.user then displaying will happen.
this.state.user.length > 0 is true
Look at this: https://reactjs.org/docs/react-component.html and https://reactjs.org/docs/conditional-rendering.html
You can right in single tag using conditional render like this
<p>{this.state.user.length ? this.state.user.user.playcount : 'loading'}
Hope this helps.
I think your problem might have something to do with the changing shape of the user value. You initialise the value to an empty array, but then—after the fetch is done—you assume it's an object (by using user.user).
Maybe you could simplify the code a bit to look more like the one below?
/* imports */
class User extends React.Component {
constructor(props) {
super(props);
this.state = {
user: null // Make it explicit there's no value at the beginning.
};
}
componentDidMount() {
let getUserInfo = axios.create(/* ... */);
getUserInfo().then(response => {
let data = response.data;
this.setState({ // No need to for a setter function as you dno't rely on the previous state's value.
user: data.user // Assign the user object as the new value.
});
});
}
render() {
let toReturn;
// Since it's now a `null`, you can use a simple existence check.
if (this.state.user) {
// User is now an object, so you can safely refer to its properties.
toReturn = <p>{this.state.user.playcount}</p>;
} else {
toReturn = <p>No data yet.</p>;
}
return <div>{toReturn}</div>;
}
}
export default User;

State is changed, but component is not refreshed

I am changing the state and I can see in the console.log the new state, however, the TextArea does not show the new state, but only when its first displayed.
Here is my implementation:
import React, {Component} from 'react';
import {TextArea} from "semantic-ui-react";
class Output extends Component {
static myInstance = null;
state = { keys:[] }
updateState(){
// this method is called from outside..
this.setState({ keys:this.state.keys.push(0) });
// I can see that keys are getting increase
console.log(this.state.keys);
}
render() {
return (
<TextArea value={this.state.keys.length} />
);
}
}
TextArea will keep showing 0, although the length of state.keys increases..
Any idea?
Never mutate the state.
To update the state, use this syntax:
this.setState(prevState => ({
keys: [...prevState.keys, newItem]
}))
you dont call your updateState function to update your state , it just exists over there, for pushing 0 to your state in order to reRender your component with new state, you can call your method in componentDidMount like below:
componentDidMount = () => {
this.updateState()
}
this will excute your updateState function immediately after component mounts into the dom.
Here is the working example
Firstly you should call your function to update the state
Example on jsfiddle
class Output extends React.Component {
static myInstance = null;
state = { keys:[] }
componentDidMount(){
this.updateState();
}
updateState() {
const newKeys = [...this.state.keys,0]
this.setState({ keys:newKeys });
}
onTextareaChange(){}
render() {
return (
<textarea value={this.state.keys.length} onChange= {this.onTextareaChange} />
);
}
}
ReactDOM.render(<Output />, document.querySelector("#app"))

Handling onchange in two stateful components in React JS

I am new to react and so confused in handling and calling the onChange events.
Now , I have 2 components :-
1. Parent component -
updateField = e => {
console.log("update field e called");
this.setState({
value: e.target.value
});
};
<InputTypeahead value={this.state.value} label="Email" onChange={this.updateField} typeaheadItems={this.emailAdressess} /
where I am calling the onChange and taking the current value out. Till
now whatever I type in Input I get the value.
Now,
2.In Child component :
I want to take the value coming from this parent component and using that would like to setstate.
How to achieve this in React js ? I have tried using refs , but result was not successful.
Any Help is appreciated.Thanks.
From version i.e 16.3.0 onwards, you can make use of getDerivedStateFromProps method to update the state based on props like
class InputTypeahead extends React.Component {
state = {
value: ''
}
static getDerivedStateFromProps(nextProps, prevState) {
if(nextProps.value !== prevState.value) {
return { value: nextProps.value};
}
return null;
}
}
According to the docs:
getDerivedStateFromProps is invoked after a component is
instantiated as well as when it receives new props. It should return
an object to update state, or null to indicate that the new props do
not require any state updates.
Before v16.3.0, you would make use of constructor along with componentWillReceiveProps like
class InputTypeahead extends React.Component {
constructor(props) {
super(props);
this.state = {
value: props.value
}
}
componentWillReceiveProps(nextProps) {
if(nextProps.value !== this.props.value) {
this.setState({ value: nextProps.value});
}
}
}

React component initialize state from props

In React, are there any real differences between these two implementations?
Some friends tell me that the FirstComponent is the pattern, but I don't see why. The SecondComponent seems simpler because the render is called only once.
First:
import React, { PropTypes } from 'react'
class FirstComponent extends React.Component {
state = {
description: ''
}
componentDidMount() {
const { description} = this.props;
this.setState({ description });
}
render () {
const {state: { description }} = this;
return (
<input type="text" value={description} />
);
}
}
export default FirstComponent;
Second:
import React, { PropTypes } from 'react'
class SecondComponent extends React.Component {
state = {
description: ''
}
constructor (props) => {
const { description } = props;
this.state = {description};
}
render () {
const {state: { description }} = this;
return (
<input type="text" value={description} />
);
}
}
export default SecondComponent;
Update:
I changed setState() to this.state = {} (thanks joews), However, I still don't see the difference. Is one better than other?
It should be noted that it is an anti-pattern to copy properties that never change to the state (just access .props directly in that case). If you have a state variable that will change eventually but starts with a value from .props, you don't even need a constructor call - these local variables are initialized after a call to the parent's constructor:
class FirstComponent extends React.Component {
state = {
x: this.props.initialX,
// You can even call functions and class methods:
y: this.someMethod(this.props.initialY),
};
}
This is a shorthand equivalent to the answer from #joews below. It seems to only work on more recent versions of es6 transpilers, I have had issues with it on some webpack setups. If this doesn't work for you, you can try adding the babel plugin babel-plugin-transform-class-properties, or you can use the non-shorthand version by #joews below.
You don't need to call setState in a Component's constructor - it's idiomatic to set this.state directly:
class FirstComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
x: props.initialX
};
}
// ...
}
See React docs - Adding Local State to a Class.
There is no advantage to the first method you describe. It will result in a second update immediately before mounting the component for the first time.
Update for React 16.3 alpha introduced static getDerivedStateFromProps(nextProps, prevState) (docs) as a replacement for componentWillReceiveProps.
getDerivedStateFromProps is invoked after a component is instantiated as well as when it receives new props. It should return an object to update state, or null to indicate that the new props do not require any state updates.
Note that if a parent component causes your component to re-render, this method will be called even if props have not changed. You may want to compare new and previous values if you only want to handle changes.
https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops
It is static, therefore it does not have direct access to this (however it does have access to prevState, which could store things normally attached to this e.g. refs)
edited to reflect #nerfologist's correction in comments
You could use the short form like below if you want to add all props to state and retain the same names.
constructor(props) {
super(props);
this.state = {
...props
}
//...
}
YOU HAVE TO BE CAREFUL when you initialize state from props in constructor. Even if props changed to new one, the state wouldn't be changed because mount never happen again.
So getDerivedStateFromProps exists for that.
class FirstComponent extends React.Component {
state = {
description: ""
};
static getDerivedStateFromProps(nextProps, prevState) {
if (prevState.description !== nextProps.description) {
return { description: nextProps.description };
}
return null;
}
render() {
const {state: {description}} = this;
return (
<input type="text" value={description} />
);
}
}
Or use key props as a trigger to initialize:
class SecondComponent extends React.Component {
state = {
// initialize using props
};
}
<SecondComponent key={something} ... />
In the code above, if something changed, then SecondComponent will re-mount as a new instance and state will be initialized by props.
set the state data inside constructor like this
constructor(props) {
super(props);
this.state = {
productdatail: this.props.productdetailProps
};
}
it will not going to work if u set in side componentDidMount() method through props.
If you directly init state from props, it will shows warning in React 16.5 (5th September 2018)
you could use key value to reset state when need, pass props to state it's not a good practice , because you have uncontrolled and controlled component in one place. Data should be in one place handled
read this
https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a-key
You can use componentWillReceiveProps.
constructor(props) {
super(props);
this.state = {
productdatail: ''
};
}
componentWillReceiveProps(nextProps){
this.setState({ productdatail: nextProps.productdetailProps })
}

Where to apply my moment() function in a react component?

I am trying to do a clock component, simply to give the date and time in local format in a webpage. I imported MomentJS using the command line npm i moment --save in my webpack environment. Next I wrote this in my Clock.jsx component (mostly based on the React example on the website).
import React from 'react';
import Moment from 'moment';
export default class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
dateTimestamp : Date.now()
};
}
tick = () => {
this.setState({dateTimestamp: this.state.dateTimestamp + 1});
console.log('tick');
}
componentDidMount() {
this.interval = setInterval(this.tick, 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
const date = this.state.dateTimestamp;
return(
<div className="clock"> Heure locale : {date}</div>
);
}
}
Doing this the timestamp incremented correctly. However, when passing a new state element in the object, the first value (based on Date.now() ) is calculated in the constructor but for each tick, only the timestamp is incrementing the formatted date is stuck on its first value. Here is the code.
import React from 'react';
import Moment from 'moment';
export default class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
dateTimestamp : Date.now(),
dateFormatted : Moment(Date.now()).toString()
};
}
tick = () => {
this.setState({dateTimestamp: this.state.dateTimestamp + 1});
console.log(this.state.dateTimestamp);
this.setState({dateFormatted: Moment(this.state.dateTimestamp).toString()});
console.log(this.state.dateFormatted);
}
...
render() {
const date = this.state.dateFormatted;
return(
<div className="clock"> Heure locale : {date}</div>
);
}
}
Does anyone could explain help me solving this issue but above all tell me what is going wrong with my piece of code?
Thanks
UPDATE: In the end my use of moment was not appropriate, even if I cannot figure out why it would not work this way. Find below my correct implementation to have the date and time refreshed every seconds.
import React from 'react';
import Moment from 'moment';
export default class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
dateFormatted : Moment().locale('fr').format('dddd Do MMMM YYYY HH:mm:ss').toString()
};
}
tick = () => {
this.setState({
dateFormatted : Moment().locale('fr').format('dddd Do MMMM YYYY HH:mm:ss').toString()
});
}
componentDidMount() {
this.interval = setInterval(this.tick, 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
const date = this.state.dateFormatted;
return(
<div className="clock"> Date (locale) : {date}</div>
);
}
}
This also "solves" the anti pattern issue exposed below (different cross-dependant setState() call). I would need the timestamp for any other reason but I will find a workaround.
#KrzysztofSztompka is correct, but I would add that maintaining two separate state variables to represent the current date as a number and as a formatted string is an antipattern. Derived state variables (i.e., state variables that can be calculated using another state variable) increases the responsibility on the developer to always keep the two state variables in sync. That may not seem too difficult in this simple example, but it can become more difficult in larger, more complicated components/apps. Instead, it is generally considered better practice to maintain one source of truth and calculate any derived values on the fly as you need them. Here's how I would apply this pattern to your example.
import React from 'react';
import Moment from 'moment';
export default class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
dateTimestamp : Date.now()
};
this.tick = this.tick.bind(this);
}
tick() {
this.setState({
dateTimestamp: this.state.dateTimestamp + 1
});
}
componentDidMount() {
this.interval = setInterval(this.tick, 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
// Calculate the formatted date on the fly
const date = Moment(this.state.dateTimestamp).toString();
return(
<div className="clock"> Heure locale : {date}</div>
);
}
}
change your tick function to this:
tick = () => {
var timestamp = this.state.dateTimestamp + 1;
this.setState({
dateTimestamp: timestamp,
dateFormatted: Moment(timestamp).toString()
});
}
This is because from 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.
Therefore in your next setState call it use old value. My proposition change this two values at once.

Categories