ReactJS: Mutating child state from parent via props - javascript

Disclaimer: I have looked at Reactjs: how to modify child state or props from parent? and do not believe the answer fits my question.
So I have a reusable, stateful conversation component that renders different DOM based on its state. I also have to be able to control which DOM is rendered from the parent.
TL;DR - How should I mutate a child components state from the parent, if at all, what other options are there?
The Child:
export default class Conversation extends Component {
constructor(props) {
super(props);
this.state = {
newConversation: props.showNewConversation
};
}
render() {
if (!this.state.newConversation) {
return (
<div>Current Conversation</div>
);
} return (
<div>New Conversation</div>
);
}
}
Now I need to render this component in various places, but I need to render the appropriate DOM depending on the parent, i.e. from the navbar you can create a new conversation, and from the users page you can go directly to your conversation with them, so I can control when I call the child via it's prop.
Calling child and setting state via prop:
<Conversation
showNewConversation={this.state.isConversationShown === true}
/>
This is currently working, but I've been told this is a very bad practice in React, my question is actually why this is considered bad practice, and what a good practice solution would look like.

In React, the general best practice is to make as many components 'state-less' if possible. Usually, state isn't necessary if you aren't loading async data or are accepting user input.
In this example the main issue happens as follows: If the parent renders
<Conversation newConversation={true} />
And later on rerenders it to
<Conversation newConversation={false} />
Then the child will not render as expected as React will 'update' the child conversation (re-render) but will not call the constructor again. Because you only transferred the props to the state in the constructor, the child will never change.
Rather most components should render only as a function of their properties. Your child can be adapted to the following:
export default class Conversation extends Component {
constructor(props) {
super(props);
}
render() {
if (!this.props.newConversation) {
return (
<div>Current Conversation</div>
);
} return (
<div>New Conversation</div>
);
}
}
Here, an update by the parent will allow the child to correctly re-render because you directly reference the props in the render() function of the child.
If you had more data about the conversation being rendered, then you should pass in all the data as a prop. i.e.
<Conversation conversationData={{name: 'abc', new: false}} />
Then the Conversation component can access the props directly in the render() method.
If you absolutely must change the state, then it should be in the form of a change in props by the parent:
export default class Conversation extends Component {
constructor(props) {
super(props);
this.state = {
newConversation: props.showNewConversation
};
}
// Response to change
componentWillReceiveProps(nextProps){
this.setState({newConversation: this.props.showNewConversation});
}
render() {
if (!this.state.newConversation) {
return (
<div>Current Conversation</div>
);
} return (
<div>New Conversation</div>
);
}
}

Related

React | Having trouble enacting state changes in child component

I am coding a checkers web app and have set up user interaction by placing onclick functions on the components. I have two different components pieces and spaces. Ideally, the onclick functions are linked to methods in the parent component which interpret the state of the component being clicked and responds by returning data back to the component or a separate component. The issue is, I do not understand how to send data to child components from the parent following the mounting of said child component. I understand you can use props to initialize the state in a child component, but is there anyway I can update the child component from the parent following this initialization? I am new to react so I'm not exactly sure how inter component communication works quite yet.
You can pass update function to child, I hope this helps.
Parent
import React, { Component } from 'react'
export default class Parent extends Component {
constructor(props) {
super(props);
this.state={
something:""
}
}
updateSomething = (anotherThing) => {
this.setState({something:anotherThing})
}
render() {
return (
<div>
{/* we are passing updateSomething function in props*/}
<ChildComponent updateSomething={(anotherThing)=>this.updateSomething(anotherThing)}/>
</div>
)
}
}
Child
import React, { Component } from 'react'
export default class ChildComponent extends Component {
render() {
return (
<div>
{/* we are using updateSomething function to update parents state*/}
<button onClick={()=>this.props.updateSomething("anotherThing")}>Update Parents state</button>
</div>
)
}
}

setting a key in a component does not re-render the component on state updates

I have a component that has two children:
A component where the user inputs and select a topic
A component that renders results based on the topic selected in the previous component
So this is the code of the parent component:
import SearchBox from "../../components/SearchBox";
import Results from "../../components/Results";
class Home extends Component {
constructor(props) {
super(props);
this.state = {
selectedTopic: null,
};
}
onSelectTopic = (topic) => this.setState({
selectedTopic: topic,
});
render() {
return (
<div>
<SearchBox
onSelectTopic={this.onSelectTopic}
/>
<Results
key={this.state.selectedTopic && this.state.selectedTopic.id}
selectedTopic={this.state.selectedTopic}
/>
</div>
);
}
}
export default Home;
Basically, the SearchBox component updates the state of the parent component that propagates the selection to the Results component.
The Results component has its own state, that I want to clear when a new selection is passed. Instead of using getDerivedStateFromProps in Results to clear its state I have added a key to <Results key={...}/> as in the snippet above.
I was expecting that every time the state in the parent component updates (ie, the onSelectTopic method is called above), the Results component will be recreated, but instead the component does not update at all. Am I missing something? If I remove its key property, Results updates as expected (ie without clearing its internal state, which is what I want to achieve by using the key)
Btw, I'm using preact instead of react, but my understanding is that the behaviour regarding the key property on components is the same.

Is it possible to re-mount a component in React?

So I have a top level component that's sending the updated props down to all child components.
One of the child components gets rendered like this in a View component like this:
const View = ({
currentSection
}) => {
console.log(currentSection.Component)
return (
<div>
<div className='main-content'>
<div className='sidenav'>
<div className='section'>
<h2 className='section-header'>My Login Page</h2>
</div>
</div>
<div>
{ currentSection.Component }
</div>
</div>
</div>
)
}
currentSection is one of items in a list of components that gets passed down to the View when ever one of the list items is clicked.
Logging the currentSection yields something like this:
{Component: {…}, id: "members", label: "Members"}
Here's an example of one of the components in the list:
class Members extends React.Component {
constructor(props) {
super(props);
this.state = {
members: props.members
};
}
render() {
const { members } = this.state;
return <MembersList members={members} />;
}
}
You can see its a simple component but the strange issue is props are getting updated but the state isn't.
So basically component isn't re-mounting. It only mounts when everything gets server rendered for the first time.
So, is there any way I can re-mount it?
From React documentation about constructor() behavior:
Note
Avoid copying props into state! This is a common mistake:
constructor(props) {
super(props);
// Don't do this!
this.state = { color: props.color };
}
The problem is that it’s both unnecessary (you can use
this.props.color directly instead), and creates bugs (updates to the
color prop won’t be reflected in the state).
Only use this pattern if you intentionally want to ignore prop
updates. In that case, it makes sense to rename the prop to be called
initialColor or defaultColor. You can then force a component to
“reset” its internal state by changing its key when necessary.
Read our blog post on avoiding derived state to learn about what to do
if you think you need some state to depend on the props.
You may need componentWillRecieveProps lifecycle method or static method getDerivedStateFromProps in react 16.3+

How to set child component state using parent component props in React 16.3? [duplicate]

This question already has answers here:
Reactjs - Setting State from props using setState in child component
(2 answers)
Closed 4 years ago.
I have been working on building an app, where an uncommon scenario has raised for me to handle. I had to pass props to a child component from the parent, and the child component needs to set those props as its state.
<ChildComponent allProps={parentProps}>
<SomeChildren withProps />
</ChildComponent>
In the ChildComponent:
class ChildComponent extends Component {
state: {
someRequiredProps: null
}
setStateForThisComponent(propsToSet) {
this.setState({someRequiredProps: propsToSet});
}
componentDidMount() {
this.props.allProps()
}
render() {
//I know this is incorrect syntax
return (<div allProps={(propsArg) => this.setStateForThisComponent(propsArg)});
}
}
Usually, I can run this method from parent component using refs, but since this ChildComponent being a very heavily reused component, I do not want to go with the refs approach. Some other solutions suggested, that I create the state in parent component itself, and pass it as props to ChildComponent, but that is not what I am trying to achieve.
Please let me solve this issue of setting the child component's state using the parent's.
If you just need to set the initial props as the state, you can do it in constructor:
constructor(props) {
super(props);
this.state = {
someRequiredProps: props.someRequiredProps,
}
}
If the state must be updated whenever the prop updates, you need to use the new getDerivedStateFromProps method:
class ChildComponent extends Component {
state: {
someRequiredProps: null,
};
static getDerivedStateFromProps(nextProps, prevState) {
return {
// return the new state based on `nextProps`
someRequiredProps: nextProps.someRequiredProps,
};
}
Also note from the docs:
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.

How can a parent alter props for a child component in react?

I'm new to react, and am a little confused about something. I've read many articles online claiming that a component cannot alter its own props, but that a parent can alter the props of its children. However, I have seen nothing that actually shows how to do this.
I would like to be able to do this:
class Parent extends Component {
constructor(props) {
super(props);
this.childProps = {name:'name',age:12,type:'child'};
this.changeChild = this.changeChild.bind(this);
}
changeChildName(newName) {
//change prop 'name' of child here;
}
render() {
return (
<Child {...this.childProps} />
);
}
}
However, I can't figure out how to do this at all - despite almost all the material that I've read on React saying that a parent can change its child's props. What I can get to work properly is as follows:
class Parent extends Component {
constructor(props) {
super(props);
this.state = {name:'name',age:12,type:'child'};
this.changeChild = this.changeChild.bind(this);
}
changeChildName(newName) {
this.setState({name: newName});
}
render() {
return (
<Child {...this.state} />
);
}
}
It seems a bit overkill to me that the Parent needs to re-render when the only thing that I want to re-render is the child view. How can I change the props of the <Child> component from the <Parent> component?
Secondary Questions
1) Is it possible to change the state of a component without having the component re-render?
2) Is there an onStateChange type event that we can tap into, or do we solely have componentWillReceiveProps?
1) Is it possible to change the state of a component without having the component re-render?
No, and it makes very little sense.
2) Is there an onStateChange type event that we can tap into, or do we solely have componentWillReceiveProps?
There is no, use componentWillReceiveProps.
It seems a bit overkill to me that the Parent needs to re-render when the only thing that I want to re-render is the child view.
Actually this is where you trick yourself: the parent IS changed, since what it returns is changed. The whole tree is being re-rendered from the root.
I think you misunderstand. It is very simple for a parent to change the child props... Just change the props that get rendered. Now, what if we want to do some changes in child without rerendering the parent. This is also pretty simple.
Attach a ref to your child and work with it. Now, the child cannot change its own props, so anything you mess with within the child will not be a prop. Props are passed down from the parent every time the parent rerenders.
So,
class Parent extends Component {
constructor(props) {
super(props);
this.childProps = {name:'name',age:12,type:'child'};
}
doSomethingOnChild() {
this.refs.child.callFunctionOfChildThatWillMessWithItself();
}
render() {
return (
<Child {...this.childProps} ref="child" />
);
}
}

Categories

Resources