I am using two components and I am using this pattern: child component should stay isolated as much it can - it is handling its own validation error. Parent component should check for errors which have dependencies between children. So, in my case: password field and password confirmation field.
Here is my code:
a) SignUp (parent)
Setting initial state.
constructor() {
super();
this.state = {
isPasswordMatching: false
};
}
In render() method I am outputting my child component. Through prop called callback I am propagating method isPasswordMatching() by binding parent's this. The goal is that method can be called within child component.
<TextInput
id={'password'}
ref={(ref) => this.password = ref}
callback={this.isPasswordMatching.bind(this)}
// some other unimportant props
/>
<TextInput
id={'passwordConfirm'}
ref={(ref) => this.passwordConfirm = ref}
...
isPasswordMatching() method is checking if passwords match (through refs this.password and this.passwordConfirm) and then updates state. Please note that this method is called inside child (password or passwordConfirm).
isPasswordMatching() {
this.setState({
isPasswordMatching: this.password.state.value === this.passwordConfirm.state.value
});
}
b) TextInput (child)
Setting initial state.
constructor() {
super();
this.state = {
value: '',
isValid: false
};
}
On blur validation is done and state is updated.
onBlur(event) {
// doing validation and preparing error messages
this.setState({
value: value,
isValid: this.error === null
});
}
Latest. Callback prop is called.
componentDidUpdate(prevProps) {
if (prevProps.id === 'password' || prevProps.id === 'passwordConfirm') {
prevProps.callback();
}
}
Issue
Somehow my refs are lost. Scenario:
Parent component is renderder
Child components are rendered
I am entering one of input fields and get out (this invokes onBlur() method) - state gets updated, child component is rendered
componentDidUpdate() is invoked and prevProp.callback() as well
When going to isPasswordMatching() method I am outputting this.password and this.passwordConfirm - they are objects with expected values of reference. Updating state of parent - component gets rendered.
Then again all children are rendered, components get updated, callback is called but this time this.password and this.passwordConfirm are null.
I have no idea why references are kinda lost. Should I be doing something differently?
Thank you in advance.
See the react documentation here, with important warnings and advise about when to use or not to use refs.
Note that when the referenced component is unmounted and whenever the ref changes, the old ref will be called with null as an argument. This prevents memory leaks in the case that the instance is stored, as in the second example. Also note that when writing refs with inline function expressions as in the examples here, React sees a different function object each time so on every update, ref will be called with null immediately before it's called with the component instance.
I'm not sure if this answers #be-codified's question for not, but I found this running into a similar issue. In my case, it turned out that it was due to using a functional component.
https://reactjs.org/docs/refs-and-the-dom.html#refs-and-functional-components
Refs and Functional Components
You may not use the ref attribute on functional components because they don’t
You should convert the component to a class if you need a ref to it, just like you do when you need lifecycle methods or state.
You can, however, use the ref attribute inside a functional component as long as you refer to a DOM element or a class component
The documentation explains what you should do to solve the issue if you have control of the component you're trying to render.
However in my case, the component was from a 3rd party library. So, simply wrapping the component worked fine.
Working
<div ref={element => this.elementName = element}>
<FunctionalComponent />
</div>
Not Working
sets this.elementName to null
<FunctionalComponent ref={element => this.elementName = element} />
Hope this helps anyone finding this question like I did.
Related
I'm learning React. I learned that props in react are readonly.
But i read somewhere:
getDerivedStateFromProps updates the state based on prop changes
So i got confused. If props are readonly, how they can change?
They are readonly in the context of a component, meaning, you can't assign a new value to them.
But a parent that passes props to child, can pass different props based on some condition. In that case, the child get rendered with a different props.
Example:
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 0,
};
}
render() {
return (
<div>
<Child prop1={this.state.value} />
<button onClick={() => this.setState({ value: this.state.value + 1 })}>Click me</button>
</div>
);
}
}
In my example, each click on the parent's button will change it state, therefore, React will re-render it, and then this parent will pass a new value to Child component.
I think you need to understand component life-cycle and how to use and handle props which is described beautifully in below links:
https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops
https://reactjs.org/docs/components-and-props.html#props-are-read-only
Props are read only means you can't change the value, but it doesn't mean you can't update it, for that you need to pass updated props back to child component and go any lifecycle methods to it.
An update can be caused by changes to props or state.
when there is update render() method is get called.
you are saying update to props, which directly means you are changing and modifying child,
props changed means, the data passed from parent is changed which leads in changing props,
always remember state passed from parent is considered as props for child is key point.
static getDerivedStateFromProps(props, state) gets called right after render() used rarely mailnly for animations transitions purposes.
the things possible with componentWillRecieveProps() and componentDidUpdate() is now done by getDerivedStateFromProps(), it will return object in response to change in props, and null if no change, sounds wonderful.
as you can see method is static you cant change any data, you can just play with your nextProps and prevState only.
this is useful for components having async api calls.
I recently wanted to upgrade my knowledge of React, so I started from the component lifecycle methods. The first thing that got me curious, is this componentWillReceiveProps. So, the docs are saying that it's fired when component is receiving new (not necessarily updated) props. Inside that method we can compare them and save into the state if needed.
My question is: Why do we need that method, if changes in props of that component (inside parent render) will trigger the re-render of this child component?
One common use case are state (this.state) updates that may be necessary in response to the updated props.
Since you should not try to update the component's state via this.setState() in the render function, this needs to happen in componentWillReceiveProps.
Additionally, if some prop is used as a parameter to some fetch function you should watch this prop in componentWillReceiveProps to re-fetch data using the new parameter.
Usually componentDidMount is used as a place where you trigger a method to fetch some data. But if your container, for example, UserData is not unmounted and you change userId prop, the container needs to fetch data of a user for corresponding userId.
class UserData extends Component {
componentDidMount() {
this.props.getUser(this.props.userId);
}
componentWillReceiveProps(nextProps) {
if (this.props.userId !== nextProps.userid) {
this.props.getUser(nextProps.userId);
}
}
render() {
if (this.props.loading) {
return <div>Loading...</div>
}
return <div>{this.user.firstName}</div>
}
}
It is not a full working example. Let's imagine that getUser dispatch Redux action and Redux assign to the component user, loading and getUser props.
It 'serves' as an opportunity to react to the incoming props to set the state of your application before render. If your call setState after render you will re-render infinitely and that's why you're not allowed to do that, so you can use componentWillReceiveProps instead.
But... you are beyond CORRECT in your confusion, so correct in fact that they are deprecating it and other Will-lifecycle hooks Discussion Deprecation.
There are other ways to accomplish what you want to do without most of those Will-lifecycle methods, one way being don't call setState after render, just use the new incoming props directly in render (or wherever) to create the stateful value you need and then just use the new value directly, you can then later set state to keep a reference for the next iteration ex: this.state.someState = someValue, this will accomplish everything and not re-render the component in an infinite loop.
Use this as an opportunity to react to a prop transition before render() is called by updating the state using this.setState(). The old props can be accessed via this.props. Calling this.setState() within this function will not trigger an additional render.
Look at this article
the componentWillReceiveProps will always receive as param "NxtProps", componentWillReceiveProps is called after render().
some people use this method use this to compare nxtProps and this.props to check, if something should happen before the component call render, and to some validations.
check the react's documentation to know more about react lifecycle!
hope this could help you!
changes in props of that component (inside parent render) will trigger the re-render of this child component
You are absolutely right. You only need to use this method if you need to react to those changes. For instance, you might have a piece of state in a child component that is calculated using multiple props.
Small Example:
class Test extends Component {
state = {
modified: "blank"
};
componentDidMount(){
this.setState({
modified: `${this.props.myProp} isModified`
});
}
componentWillReceiveProps(nextProps) {
this.setState({
modified: `${nextProps.myProp} isModified`
});
}
render() {
return <div className="displayed">{this.state.modified}</div>
}
}
In this example, componentDidMount sets the state using this.props. When this component receives new props, without componentWillReceiveProps, this.state.modified would never be updated again.
Of course, you could just do {this.props.myProp + "IsModified"} in the render method, but componentWillReceiveProps is useful when you need to update this.state on prop changes.
I just wonder if it is good that the child component updates the parent component.
in the source code, like following
class Parent extends React.Component{
state = {
name : ''
}
changeState = ((state) => {
this.setState(state)
})
submit = (() => {
// send state to api..
})
render(){
return(
<div>
<Child changeState={this.changeState} {...this.state}/>
<button onClick={this.submit} />
</div>
)
}
}
class Child extends React.Component{
change = ((e) => {
this.props.changeState({
name : e.target.value
})
})
render(){
return(
<input onChange={this.change} value={this.props.name} />
)
}
}
the reason I use this way is submitting method.
There are many input tags, and I want to bind them all together.
but I'm not sure this way is good or not.
because when I type something, parent component always will rerender.
I think it is not good.(actually it just my thinking...)
is it right?
I have used this way to update state of a parent from a child. It does work properly. But it makes the components little complex.
In your case (assuming you do this for text input elements) I don't think this will be a good practice if you are doing it for tiny input components. Because every time you hit a key on a keyboard the parent component will try to update.
But if you are wrapping a set of input elements and pass a larger object to a parent component I think that will be fine.
You could use react life cycle method shouldComponentUpdate() method to control the rendering of the parent component
shouldComponentUpdate
https://reactjs.org/docs/react-component.html#shouldcomponentupdate
shouldComponentUpdate(nextProps, nextState) {
if (this.props.name != nextProps.name) {
return true;
} else {
return false;
}
}
Here nextProps refers to the props you receive(updates) and you can refer to current prop values by "this.props"
And return true to render and false to skip the rendering.
If you have perform validations when the user inputs, then its ok.
Otherwise change 'onChange' event to 'onBlur'
Its a good idea to lift the state up and update it in the parent if multiple other siblings want to refer to the same values. You can optimise on this my making your Parent and Child Components pure as long as they don't have complex and deeply nested props and states.
According to the React docs:
React.PureComponent is exactly like React.Component, but
implements shouldComponentUpdate() with a shallow prop and state
comparison. If your React component’s render() function renders the
same result given the same props and state, you can use
React.PureComponent for a performance boost in some cases.
Re-rendering of parent is not a problem as long as it is not wasted. And Unless you are using Redux, I think this is a proper way to manage the state, i.e., inside the parent component and updating it using the child. In this way, you have made your form into a controlled component.
I think the following page will be useful to you: https://scotch.io/courses/getting-started-with-react/parent-child-component-communication
Let's say I created a component which can be turned on/off based on state.
var onOff = React.createElement(<OnOff />, mountElement);
onOff.setState({ on: false });
Later I'm creating a new component called Parent, which will use OnOff inside it.
render() { return <div><OnOff /></div> }
Now how can I change the OnOff state? There is no way I can call setState on it. And I should not according to React doc. So I have to add initial state to OnOff's props:
constructor(props) {
super(props);
this.state = { on: props.initialOn };
}
then in Parent's render method, set the initialOn prop with its state:
render() { return <div><OnOff initialOn={false} /></div> }
But it's still not working, because whenever I change Parent's state, the OnOff component inside it is not re-created with new initial state. Instead, it is only re-rendered with old state. I have a CodePen to prove it: http://codepen.io/anon/pen/QjMwjO?editors=101
You can update the state of the OnOff component by declaring the update also inside a componentWillReceiveProps function, something like:
componentWillReceiveProps:
function(nextProps) {
this.setState({
on : nextProps.initialOn
});
}
This allows you to update state, when new props arrive. And it is valid react.
You should however consider if you need state in OnOff at all: if the only initial setting and all updates ONLY come from its parent component, then a stateless component would be better.
One of the important things to understand when "Thinking in React" is to figure out which component does State belong to.
Read this in React docs
What Components Should Have State?
Most of your components should simply take some data from props and render it. However, sometimes you
need to respond to user input, a server request or the passage of
time. For this you use state.
Try to keep as many of your components as possible stateless. By doing
this you'll isolate the state to its most logical place and minimize
redundancy, making it easier to reason about your application.
A common pattern is to create several stateless components that just
render data, and have a stateful component above them in the hierarchy
that passes its state to its children via props. The stateful
component encapsulates all of the interaction logic, while the
stateless components take care of rendering data in a declarative way.
Thus, your OnOff should not have state but use properties passed down from the parent instead. I have illustrated this at http://codepen.io/anon/pen/gaxbGm?editors=101
render() {
writeLog("OnOff render called!")
writeLog("Child: " + this.props.initialOn)
return <span>{this.props.initialOn ? "On" : "Off"}</span>;
}
I would also recommend reading "Thinking in React" to get further clarity.
I would like to know difference between rendering component with state or props directly.
getInitialState:
data: this.props.data
Following code is for render function
1.
data = this.state.data
return (<Component data={data} />)
2.
return (<Component data={this.state.data} />)
3.
return (<Component data={this.props.data} />)
First two situations are crashing when I use setState on listening reflux action.
If anyone has recommendations to use other than setState or tell me the difference about those three code snippets would be very much appreciated.
Putting props in state like this:
getInitialState: function () {
return {
someKey: this.props.someKey
};
}
is an anti-pattern, unless you intend to modify the value of someKey later on and you use the prop as just an initial value.
So if you don't change the value of the prop passed in, you should go with number three.
The difference is that a component that doesn't have state can be considered "pure" (the same props passed in gives the same output, everytime) and those components are almost always easier to reason about. Duplicating the prop in state without mutating the state just gives you more lines of code in the component, and it might confuse someone else reading the code. It's a pure component disguised as an impure component.
A bit more about props and states. Props and state are related. The state of one component will often become the props of a child component. Props are passed to the child within the render method of the parent as the second argument to React.createElement() or, if you're using JSX, the more familiar tag attributes.
<MyChild name={this.state.childsName} />
The parent's state value of childsName becomes the child's this.props.name. From the child's perspective, the name prop is immutable. If it needs to be changed, the parent should just change its internal state:
this.setState({ childsName: 'New name' });
and React will propagate it to the child for you. A natural follow-on question is: what if the child needs to change its name prop? This is usually done through child events and parent callbacks. The child might expose an event called, for example, onNameChanged. The parent would then subscribe to the event by passing a callback handler.
<MyChild name={this.state.childsName} onNameChanged={this.handleName} />
The child would pass it's requested new name as an argument to the event callback by calling, e.g., this.props.onNameChanged('New name'), and the parent would use the name in the event handler to update its state.
handleName: function(newName) {
this.setState({ childsName: newName });
}