How to set one component's state from another component in React - reactjs

Say I have the following component named Home:
this.state = {
posts: []
}
componentDidMount() {
... data is fetched from api
}
<div
<PostForm />
{this.state.posts.map(post => (
<Post key={post.id} post={post} />
))}
</div>
How would I update the state of Home, or re fetch data from the api after the the PostForm component is submitted with a new Post.

Welcome to SO!
Setting the parent state from the child:
If you want your child component to have access to your parent component's state, just pass setState() as a prop in your parent class, like so...
<PostForm
setParentState={(state) => this.setState(state)}
/>
Then, later in PostForm.js, just set the parent state like so....
this.props.setParentState(newParentStateObject);
Or, you can even just do....
<PostForm
postform={this}
/>
And later on, you can call anything in postform with this.props.postform.anyFunctionEver().
Setting the child state from the parent:
Suppose you want to do the opposite now: update the state of the child component from the parent? That's just as easy, set a reference when defining <PostForm/>...
<PostForm
ref={(instance) => {this.postform = instance}}
/>
Then you can set the postform's state directly in your parent class...
this.postform.setState(newChildStateObject);
A lot can happen with the state, so if you're not sure, try making a testFunc() {console.log('test');}, and then try passing/activating this between parent and child.

Related

When Reactjs has functional component as parent and child component as class

I am new in reactjs and learning bit and pieces. I am facing an issue. The scenario is like. I have a functional component as a parent. It has a child component as a class component. I would like to set or reset the child component's state on parent's button click. Or is there any way to call the child component's any method from the parent component. I tried as
// this call from a functional component.
<PhotoPreviewUploaend setSelectedFile={setSelectedFile} ref={setImagePreviewUrl} />
Later after a button click does this:-
setImagePreviewUrl('');
I read ref attribute allows access to the component. I tried this ref between 2 class component both parent and child is class components and it works as expected. But when did the same from a functional component it has no effect at all. How can I do it?
You are not embracing react one-way data flow by using refs like that; it might not behave the way you expect;
You should pass parent state handler logic function to the child component,
then child component call it with proper value; as the result your parent state will be updated and you have nice and clean one way data flow; you can use this in any kind of component, since you don't mess with this bindings in functional components;
This example demonstrates it in action:
function App() {
// Define your state
const [someState, setSomeState] = useState(0);
return (
<div className="container">
<Child parentCallback={setSomeState} />
</div>
);
}
class Child extends Component {
render(){
return(
<div>
<button
onClick={() => this.props.parentCallback(/*someValue*/)}
>
click me!
</button>
</div>
)
}
}

useState retention scope

Components A, B and C are functional components. If someState is true and component A is visited, will the useState variables retain their values when arriving back at this module?
The return from A is handled in this main module by passing a callback function to A which resets someState.
const [someState, setSomeState] = useState()
const [anotherState, setAnotherSate] = useState()
...
const handleRemoveDetail = () => {
someState(0);
};
return (
{someState onRemoveDetail={handleRemoveDetail} ? (
<A />
) : (
<div>
<B />
<C />
</div>
)}
)
By conditionally rendering a component, you are mounting and unmounting it as the condition changes. So to answer your question, no, it will not retain its state values. State is kept during re-renders, but lost when the component unmounts.
However, if the state is managed by a parent component then we are talking about component A's props, not state. The props of component A will be the same each time it mounts and unmounts as long as the parent component has not changed them.
In addition, this syntax is incorrect, the callback should be provided like this:
return (
{someState ? (
<A onRemoveDetail={handleRemoveDetail} />
) : (
<div>
<B />
<C />
</div>
)}
)
A is not being 'visited', it is being rendered by its parent. The someState variable belongs to the parent component, and it will be available as long as the parent is retained. If you pass a setSomeState callback to any of the children and call it from there, the state in the parent will update.

How to get react grand-child node to render when child is out of my control

I have a react component which uses a 3rd party library Component as a Child node. The Grand children (or the children of the 3rd party libary) are under my control. When my component receives new props it re-renders, however the 3rd party component seems to stop my components grand-children from re-rendering also, even though the props my component received, are passed to the non-re-rendering components directly
If I remove the 3rd party component then my component re-renders as do the grand-children.
render() {
<div>
<ThirdPartyComponent props={blah}>
{this.props.products.map(prod => <MyGrandChildrenComponents product={prod} />
</ThirdPartyComponent>
</div>
}
A concrete example can be found on this code sandbox: codesandbox.io/s/silly-grass-7frlx
I'd expect my MyGrandChildrenComponents component to get updated when this.props.products changes... Any hints?
This can happen if ThirdPartyComponent is a stateful component and is not handling its prop updates correctly. One way to force a re-render is to add a key prop to your ThirdPartyComponent and update its value when a re-render is needed.
render() {
<div>
<ThirdPartyComponent key={something-that-changes-when-rerender-needed} props={blah}>
{this.props.products.map(prod => <MyGrandChildrenComponents product={prod} />
</ThirdPartyComponent>
</div>
}
If ThirdPartyComponent is a PureComponent it should re render (and so re render its children) when one of its props changes so you can try:
render() {
<div>
<ThirdPartyComponent props={blah} products={this.props.products}>
{this.props.products.map(prod => <MyGrandChildrenComponents product={prod} />
</ThirdPartyComponent>
</div>
}
To trigger re renders when this.props.products changes.
But any prop should do.
If ThirdPartyComponent has a custom implementation of shouldComponentUpdate, then you will have to find the specific prop which triggers update if it exists.

Pass data between siblings without Redux

Consider the following component tree
<Header />
<SearchBar />
<ProductList />
<Product />
In the SearchBar component I want to catch the value of an input and send that value to ProductList to dynamically render Product components based on the value received.
Is there a way to communicate between SearchBar and ProductList without the need of a component that wraps both of them or without Redux ?
According to React's page "React is all about one-way data flow down the component hierarchy" which means that data passing horizontally should be avoided.
In your case you could have a parent component that render <SearchBar/> and <ProductList/>based on state. For example, whenever the user enter with a value in <SearchBar> it changes the state on the parent component, and consequently <ProductList> will be rendered again.
As Osman said, you could pass the value of the SearchBar into a state and then from the state into the Product using a function.
handleChange(event) {
this.setState({
newProduct: event.currentTarget.value
)}
}
render() {
return(
<SearchBar onChange={this.handleChange} />
<Product content={this.state.newProduct} />
);
}
Make sure you don't forget to bind the function in the constructor.

Calling this.refs.component.function

So i have a navigation component at a higher level like this:
<Navigation ref="nav">
<Children>
<Footer>
In one of my children components I want to call a method inside my Navigation component. How would I do so? I gave this a shot:
if Navigation has a method called
toggle() {
this.setState({open: true})
}
could I call it like this in the children component?
Children:
openSomething = () => {
console.log(this.refs);
this.refs.nav.toggle();
}
<Somebutton onClick={openSomething} name="OpenStuff" />
After i had done this, refs had come out undefined, so I wasn't sure how to go about this. The idea is that i have a child component that I want to open a modal on button click, but it is its child and I thought of doing it this way, however it didnt work.
You can't do this from Children because Navigation is its sibling. Only their parent can refer to Navigation via refs. You can trigger an event from Children which is handled by the parent:
<Somebutton onClick={this.props.onOpen} name="OpenStuff" />
And then handle it in the parent component:
render() {
return (
<div>
<Navigation ref="nav" />
<Children onOpen={() => this.handleOnOpen()} />
</div>
)
}
handleOnOpen() {
this.refs.nav.toggle()
}

Resources