Passing Event Method to Grandchild Component - reactjs

I'm a bit new to React, so forgive me if this is a bit of a newb question.
I have a base component (Page) which uses state to control whether or not a modal popup is displayed:
constructor(props) {
super(props);
this.state = {
showModal : false,
modalContent : 'Initial Modal Content'
};
this.showModal = this.showModal.bind(this);
this.hideModal = this.hideModal.bind(this);
}
showModal(modalContent) {
this.setState({
showModal : true,
modalContent : modalContent
});
}
hideModal(e) {
this.setState({showModal : false});
}
My problem is that I want a grandchild component to be able to open up my modal.
I know I can do this by passing the state to the child component and then to the grandchild component:
<PartnersTable showModal={this.showModal} partners={PARTNERS} />
That just seems a bit sloppy to me, but maybe that's just the React way.
Can someone let me know if I'm doing this properly or if there's a cleaner way to do this?
You can view my full app on GitHub: https://github.com/CCChapel/Thy-Kingdom-Come/blob/master/react.js
Thanks!
-Eric

You're doing it correctly.
In React the only way for a parent to pass props/state to it's children is by passing it downwards. React is unidirectional from top to bottom; from parent to child only.
So your assumption is correct. It can get pretty sloppy. You must know that React is for presenting the UI and simple cases of state management. When you're application gets more complex and you need to pass down state in a direct and simplified manner use Redux, MobX or any other state containers out there.
If you don't like the complexity of passing down props down the tree considering using Redux (I use Redux myself).
Consider the following resources:
https://egghead.io/courses/getting-started-with-redux
http://redux.js.org/docs/introduction/
https://github.com/reactjs/redux/tree/master/examples
To learn how React works though get used to using only React first then use a state container when the application gets more complex.

In order to achieve that you need to pass your showModal method as a prop to child component(s) in which you want to trigger the visibility of the modal. This is how the render method of the parent component should be:
render() {
return(
<ChildComponent showModal={this.showModal}>
)
}
Then in you child component:
class ChildComponent extends Component {
render() {
return(
<input type="button" onClick={this.props.showModal} value="show modal"/>
)
}
}

Related

Sharing state among subcomponents in React

I'm currently developing an app that uses React in some parts of its UI to implement some complex feature that are very interactive. Some of these parts of the app, that are in fact React components, have become very complex in size.
This has led me to organize the code by dividing them in multiple subcomponents (and those in their own subcomponents, and so forth). The main problem is that some of the grandchildren of the main component might have the need to modify the state of another grandchildren that they're not related to. To do so, I end up having to have the state in the common parent and passing a lot of event handlers (methods of the parent component) to their children, and those children would need to pass it to their grandchildren.
As you can imagine, this is becoming some kind of a mess.
// MyComponent.js
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
list: [1, 2, 3, 4],
selected: '',
}
this.add = this.add.bind(this)
this.handleChange = this.handleChange.bind(this)
}
add() {
const newNumber = this.state.list[this.state.list.length - 1] + 1,
list = [...this.state.list, newNumber]
this.setState({list})
}
handleChange({target}) {
this.setState({
selected: target.value,
})
}
render() {
return (
<>
<List items={this.state.list} selected={this.state.selected} />
<Button onClick={this.add} />
<input type="text" value={this.state.selected} onChange={this.handleChange} />
</>
)
}
}
// Button.js
class Button extends React.Component {
render() {
return (
<button onClick={this.props.onClick}>Click me!</button>
);
}
}
// List.js
class List extends React.Component {
constructor(props) {
super(props)
this.refs = props.items.map(_ => React.createRef())
}
render() {
return (
<ul>
{this.props.items.map((item, key) =>
(<li ref={this.ref[key]} key={key}>{item}</li>)
)}
</ul>
);
}
}
In the previous dummy code you can see how I need to define the add() method in the MyCompoent component so that an action that happens in the Button component can modify what is being shown in List. Even tho this might seem like the obvious way to do it, my component has a big component tree, and a lot of methods, and most of then are lost in the tree, passing from parent to child until it reaches the component that should be expected.
I have done some research on the internet and it turns out this is a very common problem. In most sites, using Redux or other state management library is recommended. However, all the tutorials and guides I've seen that implement Redux with React seem to assume you're only using React to build your app, in Single Page Application sort of way. This is not my case.
Is there any way to share the state of a component to avoid this kind of problem? Is there, maybe, a way to use Redux multiple times for multiple components in the same app, where one store saves only the state for MyComponent and can be accessed by either List or any of its possible children?
Redux doesn't require your entire site to be in React. It implements a higher-level component that you can use with any React components even if they are embedded in another site.
You can look at React hooks to solve similar problems. Specifically, check out useContext() and useState().
You've used a lifting state up pattern in react in your example.
It's quite common you good approach but when you app is growing you need to pass all bunch of props throu the tree of components. It's difficult to maintain.
In this case you need to check out redux with separated store or useContext() hook.

React: Passing child state to parent state

I am working since more than a year with React and i have read Thinking in react, Lifting state up, and State and lifecycle.
I have learned that React's concept with data flow is is One-way data flow.
Citates from these pages:
React’s one-way data flow (also called one-way binding) keeps everything modular and fast.
Remember: React is all about one-way data flow down the component hierarchy. It may not be immediately clear which component should own what state. This is often the most challenging part for newcomers to understand, so follow these steps to figure it out:...
If you imagine a component tree as a waterfall of props, each component’s state is like an additional water source that joins it at an arbitrary point but also flows down.
As i understand this, following example is not allowed because i am passing child state data to the parent. But i see some developers working like that:
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { fromParent: null };
}
addSomething(stateValueFromChild) {
this.setState({fromParent: stateValueFromChild});
}
render() {
return <Child
addSomething={(stateValueFromChild) => this.addSomething(stateValueFromChild)}>
// ...
</Child>;
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.state = { fromChild: 'foo' };
}
render() {
return <Form onSubmit={() => this.props.addSomething(this.state.fromChild)}>
// ...
</Form>;
}
}
My questions now are:
Is this really not allowed?
Why should this not be modular and fast?
Is this really braking the one-way-dataflow, becoming a two way dataflow?
What other problems could happen with this way?
When i would lift the state up, how would you solve following case; 50 concrete parents that uses that child component, should every parent have a same initialized sub-state for the same child that they are using?
Is this really not allowed?
Why should this not be modular and fast?
Excellent questions. This is allowed. It's just a bit tricky to make it work right because you've got state synchronization here. In the modern frontend world, the state synchronization is believed to be a very challenging task.
The problem will appear when you'll need to sync the state in two directions. For instance, if this child form is used to edit some element of the list, and you're changing the current element of the list. You'll need the child component to detect that situation and sync its local state with the new element from props during the particular UI update. As long as you don't have that, you're fine.
Is this really braking the one-way-dataflow, becoming a two way dataflow?
Nah, it's not. It's still unidirectional data flow because React can't work in any other way by design; UI updates always coming from top to bottom. In your example, your child triggers an event which causes the parent to update its state (it's totally fine), which will cause the UI update of the parent and the child. If you really violate "unidirectional data flow", you'll feel it. You will get an infinite loop or something similar.
When i would lift the state up, how would you solve following case; 50 concrete parents that uses that child component, should every parent have a same initialized sub-state for the same child that they are using?
Yep, that's what they mean by "lifting the state". You organize your root state as a tree reflecting the state of the children, then pass down elements of the state to children along with callbacks to modify the root state.
It's allowed and there is nothing wrong with your code, but I would not call it passing state from child to parent. All you do is invoking method passed in props and triggered by event with some argument, which in your example is child's state value, but it could be any other variable. Parent component knows nothing about nature of this argument, it just receives the value and able to do anything with it, for example change it's own state to another. If Child's state will change, Parent is not going to receive this update without onSubmit event fired again. But children always receive updates from the parent and automatically get rerendered, when props get changed. And of course some of the props could be states of some parents. Here is the major difference in behavior.
There is a good article explaining this in details: Props down, Events Up
Your question is absolutely correct, many times developer (including myself) struggled for passing child's components state or props to parent component.
I always do logic for getting next state or next props in child component and pass next state or next props to parent component by using handler functions of parent component.
import React, { Component } from "react";
import { render } from "react-dom";
class Parent extends Component {
constructor(props) {
super(props);
this.handleSomething = this.handleSomething.bind(this); // binding method
this.state = {
fromParent: "foo"
};
}
handleSomething(value) {
this.setState(prevState => {
return {
fromParent: value
};
});
}
render() {
return (
<div>
<h1>State: {this.state.fromParent}</h1>
<Child handleSomething={this.handleSomething} />
</div>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.state = {
fromChild: "bar"
};
}
render() {
return (
<div>
<button
onClick={e => {
const { fromChild } = this.state;
// do whatever as per your logic for get value from child pass to handleSomething function
// you can also do same for handling forms
this.props.handleSomething(fromChild);
}}
>
Click Me
</button>
</div>
);
}
}
render(<Parent />, document.getElementById("app"));

How to change state of component from anywhere without Redux?

Is this bad practices or not ?
export state change function from component
import it from other file.
call the function to change state?
In this way we can change some component state from anywhere.
For example...
We want to change the Model.js state from anywhere.
Modal.js
import React from 'react';
export let toggleModal;
export default class Modal extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false,
};
toggleModal = this.toggleModal;
}
toggleModal = () => {
this.setState({ open: !this.state.open });
};
render() {
const { open } = this.state;
return <div style={{ color: 'red' }}>{open && 'Hello Modal'}</div>;
}
}
App.js(Some Top Level component)
import React from 'react';
import Modal from './Modal';
export default () => (
<>
...
<Modal />
...
</>
);
Somewhere.js
import React from 'react';
import {toggleModal} from './Modal';
export default () => (
<>
<h1>Hello!</h1>
<button onClick={() => toggleModal()}>open Modal!</button>
</>
);
  
But there is no reference in React Official docs, so is this bad practices ?
What React Docs recommends...
Just passing function props to change parent state from parent to children
Use context
Redux or Mobx
But, these are too complex for me.
Example code here
https://next.plnkr.co/edit/37nutSDTWp8GGv2r?preview
Everything seems pretty much overwhelming and difficult at the beginning. But as we get out hands on them, it's give us more confidence to dig into.
I would recommend to use redux that's how we tackled props drilling problem. You can dispatch a action and connect reducer to corresponding component which upon updating state will re render. This is what I recommend to most of the people to learn the tale of redux with a real life example:
Understanding Redux: The World’s Easiest Guide to Beginning Redux
Apart from this you can take Dan Abramov, author of the library, free redux course on egghead.io:
Getting Started with Redux
The problem you run into, almost immediately like your code example does is this:
It will not work: your toggleModal() method expects a this to refer to an actual component instance. When your onClick() handler fires you invoke toggleModal() as a plain function. The this context will be wrong, and so at best (in your example) you will get an error because you try to invoke something undefined, at worst (in general) you end up invoking the wrong method.
When you think about it, for any non-trivial React component you will have a hard time obtaining a reference to the actual instance that is currently being used: you have to make sure that you are not forgetting to invoke the method on the right component instance and also you have to consider that instances may be created/destroyed 'at will' for whatever reason. For example: what if your component is rendered indirectly as part of some other component's render() method? Multiple layers of indirection like that make it even harder.
Now, you could fix all that by abusing ref with abandon but you will find that now you have to keep track of which ref refers to what particular instance, if you happen to have multiple of the components to consider in one render tree...
Whenever you think one component needs to handle the state of its siblings, the solution is usually to lift the state one level up.
export default class Modal extends React.Component {
render() {
const { isOpen } = this.props;
return <div style={{ color: 'red' }}>{isOpen && 'Hello Modal'}</div>;
}
}
export default class Home {
this.state = {
isOpen: false,
};
toggleModal = () => {
this.setState({ isOpen: !this.state.isOpen });
}
render() {
const { isOpen } = this.state;
return (
<>
<h1>Hello {name}!</h1>
<button onClick={() => this.toggleModal()}>open Modal!</button>
<Modal isOpen={isOpen}/>
<p>Start editing and see your changes reflected here immediately!</p>
</>
)
}
}
This way the Home handle the state and your problem is solved.
This can get annoying if the state needs to be "drilled down" to children, that's a problem than redux or react-context can solve.
Here <Modal /> is the child component. So to call a function in a child component you can simply use Ref.
You can refer this page to get more info about Ref.
You can assign a class variable as a ref to this child and use this class variable as an object to call its function.
I found if in special case, my way is okay.
Special case means something like customAlert component.
It is okay only one instance of customAlert component mounted at a time in App.
To achieve this...
1.Use ref to access and change DOM
2.attach state changing function or component to window and call window.function
3.my case: export state changing function and import it from other file.
And here is how to do with react Context
https://next.plnkr.co/edit/EpLm1Bq3ASiWECoE?preview
I think Redux is overkill if the main thing you are interested in is to make some states-like data available and updatable throughout your App without props drilling.
For that purpose, a much simpler approach (maybe not available at the time the question was posted?) is to use react context: https://frontend.turing.edu/lessons/module-3/advanced-react-hooks.html
"context - an API given to us by React, allowing for the passing of
information to child components without the use of props
[...]
useContext - a react hook, allowing functional components to take
advantage of the context API"

child component update parent component

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

What is the proper way to handle React state and props

I've been using React for a while, and I've tried many different approaches to do this, they all have their advantages and disadvantages so I'd like to clarify which one is the way to go.
So I have this example scenario:
- A (smart) parent component, listening to a flux store
- A (dumb?) child component being rendered by the parent component above only rendering a view and having some "small internal logic", and by that I mean, some logic that doesn't make sense to be handled by an external action, like onChange events that update its input value, for example.
Should I only pass whatever I wanna pass to the child component as props, and don't mess with its state. So whatever small logic I need to do I update its props directly (even tho I know it's not recommended to update props directly)?
Or
I pass whatever props I wanna pass to the child component and then "get them" in the getInitialState, do that small logic using its now state variables?
The problem with the second approach is that when I actually send an action from the child component, and the parent component listens to the stores results and gets updated, and I have a hard time re rendering the child component now, unless I change its key value, which I also think it shouldn't be done this way.
And the first approach, even tho I'm changing props, which, like I said, I don't think it's also the best thing to do this, I don't have a problem re rendering the child component after sending an action that updates the store that the parent component is listening to.
Probably there are other ways to do it, and for sure a better way. And that is what I'd like to see. Let me know if the question is confusing and I'll try explaining in some other way.
Thanks!
You should only set state from the main (parent) component. All children components should be "dumb" components. If you need to manipulate state from a child component...have a function in the parent component that modifies the state needed...then pass that function as a prop to the child. When the action needed to update the state in the child is completed, call the function passed in as a prop which will call it in the parent component and will update state accordingly.
Below is just some boilerplate code to give you an idea as to what I'm talking about.
Example child component:
import React, {Component} from 'react';
class Child extends Component {
edit = () => {
var state = "string";
this.props.edit(state);
}
handleChange = (evt) => {
this.props.inputChange(evt.target.value);
render() {
return (
<button type="button" onClick={this.props.edit}>Click Me!</button>
<input type="text" name="name" onChange={this.handleChange}>
)
}
}
export default Child;
Example parent component :
import React, {Component} from 'react';
import Child from './Child';
class Parent extends Component {
edit = (val) => {
this.setState({data: val})
}
inputChange = (val) => {
this.setState ({input: val});
}
render() {
return (
<Child edit={this.edit} inputChange={this.inputChange}>
)
}
}
export default Parent;

Resources