how children's props get updated? - javascript

I'm new to React, still struggling to learn how props get updated and in which lifecycle methods the update takes place. Below is some code I wrote:
export default Parent extends Component {
constructor(props) {
super(props);
this.state = {
name: "Tom"
}
}
changeName = () => {
this.setState({ name: "Jerry" });
}
render() {
return <div>
<Child name={this.state.name}/>
<button onClick={this.changeName }>Change Name</button>
</div>
}
}
export default Child extends Component {
...
shouldComponentUpdate(newProps, newState) {
console.log("old name is" + this.props.name)
console.log("new name is" + newProps.name)
return true;
}
render() {
return <p>{this.props.name}</p>
}
}
When I click the button, the content of paragraph changes from "Tom" to "Jerry", which is expected, but if I dig a little bit, the console showed that:
old name is Tom
new name is Jerry
My question is, after I click the button, inside the Child component, this.props.name is still Tom, which means this.props is not the latest props, while inside the render() function, this.props.name becomes Jerry, which means this.props is the latest prop.
When did the old prop change to the latest prop? Is there another lifecycle method after shouldComponentUpdate() that actually changes this.props to the latest props, for example:
afterShouldComponentUpdate(newProps, newState){
...
this.props = newProps;
}
Does such a lifecycle method exist? I know the name won't match, but is there a lifecycle method that accomplishes the above functionality?

No, there is no such lifecycle method.
Assignment of props to this is happening BETWEEN lifecycle methods (after shouldComponentUpdate, before render) by the library itself and there is no public interface how you can interfere with the process.

Related

Why ref object existed before assign it to target in Reactjs

I tried Ref and forwarding-refs in Reactjs to keep a reference to a DOM or React Component. I did not understand why the ref object in which created kept correct target reference before target component was rendered or mounted.
I have the codesandbox right here to present my question more details. Here is the screenshot
As what you see, Ref object keep a correct reference to the target (in this case is FancyButton Component) even the render and ComponentDidMount method of target have not yet fired.
Could someone possibly help me to understand about this more. Thanks.
Because console keep reference to current value to object (so you get value of object after rendering). If you will change your code to
console.log(JSON.stringif(ref))
Then you will get this:
]1
You are console logging in quite a few wrong places, namely the console.log("render of Fancybutton"); in the render method of FancyButton and in the function body of forwardRef of the FancyHOC. The render method should be a pure function without side-effect. Console logging is considered a side-effect.
Study this react component lifecycle diagram:
Notice where the render function resides. It resides in the "Render Phase" of the render cycle. Notice also that it "May be paused, aborted or restarted by React." This means they are called before anything in the "Commit Phase".
Notice now as well that the other component lifecycle methods, specifically componentDidMount and its functional component coutnerpart useEffect with empty dependency array are all called after the component has rendered (committed to DOM) at least once.
Fix the logging in the incorrect places:
FancyButtonHOC
Move the logs into componentDidUpdate and useEffect hook.
function createFancyButtonHOC(WrappedComponent) {
class FancyHOC extends React.Component {
render() {
const { forwardRef, ...rest } = this.props;
return <WrappedComponent ref={forwardRef} {...rest} />;
}
componentDidMount() {
console.log("FancyHOC mounted");
}
componentDidUpdate() {
console.log("render of HOC: ", this.props.forwardRef);
}
}
return React.forwardRef((props, ref) => {
React.useEffect(() => {
console.log("forwardRef callback: ", ref);
});
return <FancyHOC forwardRef={ref} {...props} />;
});
}
In FancyButton if you leave the console log in the render method it will simply log any time react is invoking the render method for DOM diffing purposes, not actually when it is rendered to the DOM during the commit phase. Move it to componentDidUpdate.
export default class FancyButton extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
componentDidMount() {
console.log("fancy button mounted");
}
componentDidUpdate() {
console.log("render of Fancybutton");
}
handleClick() {
console.log("button click");
}
render() {
return <button onClick={this.handleClick}>Click me</button>;
}
}
New console log output
render of HOC: {current: FancyButton}
fancy button mounted
FancyHOC mounted
forwardRef callback: {current: FancyButton}

Accesing the state of child component

I have a parent component which contains a function, which when called needs to acces the childrenĀ“s component state. I dont want to move the whole state to the parent component because i want the children component to be independent. What is the cleanest and most recommended way to achieve this?
class ParentComponent extends Component {
render() {
return (
<div>
<ChildComponent/>
<SaveButton onClick={this.saveFunction}/>
</div>
)
}
saveFunction = () => {
//Here i need to acces the child Component state
}
}
My solution so far was that everytime something changed in child component i called a function which was passed from the parent Component. Like this:
class ChildrenComponent extends Component {
state = {
name: "David",
age: 19
}
render() {
return (
//inputs with the inputChange function
)
}
inputChange = (e) => {
//Update the state
//Then pass the state to the parent
this.props.passStateToParent(this.state)
}
}
I would recommend to look up some of the React patterns - especially Render Props, as it allows to expose the state and wanted methods of a component - what you want in this situation.
Best of luck!
You can make a function in parent component and pass it down to child component as prop. This function could return to parent component the state of your child component. See more here: https://reactjs.org/docs/faq-functions.html
You cannot directly access the state of the child component,this can be done by passing the state to methods of parent component which are passed as props to child component,the following example demonstrate how to do it .
class ParentComponent extends React.Component {
somefunc() {
//do your action
}
render() {
<ChildComponent parentfunc={this.somefunc}/>
}
}
class ChildComponent extends React.Component {
constructor(props){
super(props)
this.state = {somedate:'value'}
this.func = this.func.bind(this)
}
func() {
this.props.parentfunc(this.state)
}
render() {
<button onClick={this.func}>text</button>
}
}

Pass value to parent component in React

essentially I have a form component for users to fill out. When they complete an action: onChange={onChange} it returns a value (child component);
function onChange(value) {
console.log("This is: ", value);
}
I want to pass the value to a state in the parent component (so that the from can be processed and form data sent). How can I do this? My constructor looks something like this (parent component);
constructor(props) {
super(props);
this.state = {
form: {
name: '',
age: '',
value: ''
}
};
}
Trying to learn react, please go easy as I'm unsure of how to do this. Would love any feedback! Thanks!
Please read the official documentation start guide carefully and patiently when you are a starter.
In react component, state is the internal state and props is the state passed from outside.
You could only modify the state by setState method.
As for the question you ask, you could define a callback function which call setState in parent component, then pass the callback function as props into the child component.
As Zhili said, you should define a function in your parent component that is passed as prop to the child component.
Here's a brief example:
const Child = ({ onSubmit }) => (
<form onSubmit={onSubmit}>
{ /* your <input/>'s here ... */}
</form>
);
class Parent extends React.Component {
state = {
value: null,
};
onChildSubmit = (value) => {
this.setState({
value,
});
}
render() {
return (
<div class="Something">
<Child onSubmit={} />
</div>
)
}
}

React: Assigning State Passed as Prop to Component as Variable Prevents Update?

Suppose we have the following setup with a parent component with two children C1 and C2:
Example: container for C1 and C2, with a state called data
-C1: input, updates state in Example through handler passed as propdisplay, shows state from Example
-C2: display, shows state from Example
Here it is in code and codepen:
class Example extends React.Component {
constructor (props) {
super(props)
this.state = { data: 'test' }
}
onUpdate (data) { this.setState({ data }) }
render () {
return (
<div>
<C1 onUpdate={this.onUpdate.bind(this)}/>
<C2 data={this.state.data}/>
</div>
)
}
}
class C1 extends React.Component {
constructor(props) {
super(props);
this.onUpdate = this.props.onUpdate;
}
render () {
return (
<div>
<input type='text' ref='myInput'/>
<input type='button' onClick={this.update.bind(this)} value='Update C2'/>
</div>
)
}
update () {
//this.props.onUpdate(this.refs.myInput.getDOMNode().value);
this.onUpdate(this.refs.myInput.getDOMNode().value);
}
}
class C2 extends React.Component {
constructor(props) {
super(props);
this.data = this.props.data;
}
render () {
return <div>{this.props.data}</div>
//return <div>{this.data}</div>
}
}
/*
* Render the above component into the div#app
*/
React.render(<Example />, document.getElementById('app'));
Notice that in C2's constructor, we have a reference to this.props.data. If we set that variable as a class attribute like this.data = this.props.data React fails to update C1 even after we click the update button and Example's this.state.data has been changed. I have commented out the line that works, which references this.props.data directly.
My first idea is that this must be illegal syntax in React. However, further testing with C1 showed that if the props passed in is a function and not state, there is no problem (see the code under C1's update function to confirm what I am talking about).
Why does this not work for state passed in as props but works for functions passed in as props? I would assume Example sees that C1 has changed data state and as a result of this, call a re-rendering of C2 which uses this.data to figure out what to render next.
Because the constructor only gets called once and not every time it gets new state or props so your class variables references the props passed initially and not the new ones because it doesn't rerun the constructor again. See constructor
The constructor for a React component is called before it is mounted
So once it's mounted, it doesn't get called again. The functions on the other hand, especially pure functions, will work fine because you didn't modify the function itself or any values within it.
If you want to update the class variables based on props change you might want to check shouldComponentUpdate or componentWillReceiveProps
So example in your C2 component, to fix it use this:
componentWillReceiveProps(nextProps) {
this.data = this.nextProps.data
}
But I think it's redundant doing that, this.props works fine most of the time.

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 })
}

Categories