How to pass Grandchild data into GrandParent Component without passing data to intermediate components in Reactjs?

2.2k views Asked by At

i have created three components Aboutus,AboutusChild & GrandChild and now i want to pass grandchild state value in my grandparent component that is "Aboutus" component but without using intermediate component(AboutusChild), is it possible in react js without using redux state management library.

i dont' want to use redux right now until some data-communication concept are not clear.

class AboutUs extends Component {
            constructor(props) {
                super(props)`enter code here`
                this.state = {
                    childState: false
                }
            }
            myMehtod(value) {
                this.setState({
                    childState: value
                })
            }
            render() {
                console.log('AboutUs', this.state.childState)
                return (
                    <div>
                        <h1 className={`title ${this.state.childState ? 'on' : ''}`}>
                            About us {this.props.aboutusProp}
                        </h1>               
                        <AboutUsChild />
                    </div>
                )
            }
        };
    class AboutUsChild extends Component {
    myMehtod(value) {
        this.setState({
            childState: value
        },
            this.props.myMehtodProp(!this.state.childState)
        )

    }
    render() {
        console.log('AboutUsChild', this.state.childState)
        return (
            <div>
                <h1 className={`title ${this.state.childState ? 'on' : ''}`}>About Us Child</h1>
                <GrandChild>
                    without function
                </GrandChild>
                <h1>About Us Child</h1>
                <GrandChild Method={true} myMehtodProp={this.myMehtod.bind(this)} />
            </div>
        )
    }
};
    class GrandChild extends Component {
        constructor() {
            super()
            this.state = {
                TitleClick: false
            }
        }
        TitleClick() {
            this.setState(
                {
                    TitleClick: !this.state.TitleClick
                },
                this.props.myMehtodProp(!this.state.TitleClick)
            )

        }
        render() {
            console.log('GrandChild', this.state.TitleClick)
            return (
                <div>
                    {this.props.Method ?
                        <h1 onClick={this.TitleClick.bind(this)} className={`title ${this.state.TitleClick ? 'on' : ''}`}>Grand Child</h1>
                        : null}
                    <h1>{this.props.children}</h1>
                </div>
            )
        }
    };
2

There are 2 answers

0
David Vass On

There's really no way to pass child state up without passing it to some callback. But frankly speaking this does not seem like a good design choice to me. You should always have common state on top of all components consuming that state. What you can do, is to pass stuff as children:

class SomeComponent extends React.Component {
  ...
  render() {
    const { value } = this.state;

    return (
      <Child value={value}>
        <GrandChild value={value} onChange={this.handleValueChange} />
      </Child>
    );
  }
}

const Child = ({ value, children }) => (
  <div>
    <h1 className={`title ${value ? 'on' : ''}`}>About Us Child</h1>
    {children}
  </div>
)

const GrandChild = ({ value, onChange }) => (
  <div>
    <h1 onClick={onChange} className={`title ${value ? 'on' : ''}`}>Grand Child</h1>
  </div>
);

This way you got control from parent component of everything. If this is not the way, because you are already passing children, and for some reason you want to keep it this way, you can pass "render" prop:

// JSX in <SomeComponent /> render function:
<Child
  value={value}
  grandChild=(<GrandChild value={value} onChange={this.handleValueChange} />)
>
  Some other children
</Child>

...

const Child = ({ value, grandChild, children }) => (
  <div>
    <h1 className={`title ${value ? 'on' : ''}`}>About Us Child</h1>
    {grandChild}
    {children}
  </div>
)

If you want to be more fancy and there will more than few levels of nesting, you can always use context (highly recommend reading docs before using):

const someContext = React.createContext({ value: true, onChange: () => {} });

class SomeComponent extends React.Component {
  ...
  render() {
    const { value } = this.state;
    return (
      <someContext.Provider value={{ value: value, onChange: this.handleValueChange }}>
        <Children>
      </someContext.Provider>
    );
  }
}

...

const SomeDeeplyNestedChildren = () => (
  <someContext.Consumer>
    {({ value, onChange }) => (
      <h1 onClick={onChange}>{value}</h1>
    )}
  </someContext.Consumer>
)

I would pick first two, if your structure is not that complex, but if you are passing props deeply, use context.

0
Dor Shinar On

The only way to do something of this sort without external library would be leveraging React's Context API, although bare in mind that it is not nearly as robust as redux or mobX.