DEV Community

Cover image for Lifting up information in React from one component to its parents in the component tree.
roggc
roggc

Posted on

Lifting up information in React from one component to its parents in the component tree.

In this post, I will show you how to lift up information from one component to its parents in the hierarchy of the component tree.

First, let's start by the innermost component definition, where we have the information we want to lift up:

import React,{useState,useEffect} from 'react'
import * as styles from './index.module.css'

const Some=({liftUpInfo})=>{
    const [value,setValue]=useState(false)

    useEffect(()=>{
        liftUpInfo.bind(null,setValue)()
    },[])

    return (
        <div className={styles.general}>
            {value&&'😄'}
            {!value&&'😭'}
        </div>
    )
}

export default Some
Enter fullscreen mode Exit fullscreen mode

As you can see, we receive as props a liftUpInfo function. We use this function to effectively lift up information to the parent of the component. We use useEffect hook to do so, binding the value or information we want to lift up in the call of the function.

Now let's see the definition of this liftUpInfo function. To do so let's see the definition of the parent component:

import React,{useRef,useState,useEffect} from 'react'
import Some from '../Some'
import * as styles from './index.module.css'

const Other2=({liftUpInfo})=>{

    const [foo,setFoo]=useState(false)

    const information=useRef(null)

    const catchInfo=(info)=>{
        information.current=info
        setFoo(prev=>!prev)
    }

    useEffect(()=>{
        liftUpInfo.bind(null,information.current)()
    },[])

    return (
        <div className={styles.general}>
        <Some liftUpInfo={catchInfo}/>
        </div>
    )
}

export default Other2
Enter fullscreen mode Exit fullscreen mode

So you see how the liftUpInfo function in the child received as a prop it's the catchInfo function defined in the parent.

What this function does in the parent it's to update the value of a reference value. To do so we force a re-render of the component by setting its local state (foo). After the re-render, the useEffect hook takes in action and passes the info up to the next parent.

In the next parent we have exactly the same:

import React,{useEffect,useState,useRef} from 'react'
import * as styles from './index.module.css'
import Other2 from '../Other2'

const YetAnother=({liftUpInfo})=>{
    const [foo,setFoo]=useState(false)

    const information=useRef(null)

    const catchInfo=(info)=>{
        information.current=info
        setFoo(prev=>!prev)
    }

    useEffect(()=>{
        liftUpInfo.bind(null,information.current)()
    },[])

    return (
        <div className={styles.general}>
            <Other2 liftUpInfo={catchInfo}/>
        </div>
    )
}

export default YetAnother
Enter fullscreen mode Exit fullscreen mode

Nothing to comment in here because it's exactly the same as before.

Now, finally, in the next component (parent), we will make use of the lifted information:

import React, { useRef,useState } from 'react'
import YetAnother from '../YetAnother'
import * as styles from './index.module.css'

const Any=()=>{
    const [foo,setFoo]=useState(false)

    const information=useRef(null)

    const catchInfo=(info)=>{
        information.current=info
        setFoo(prev=>!prev)
    }

    const clicked=(setValue)=>{
        setValue(prev=>!prev)
    }

    return (
        <div className={styles.general}>
        <YetAnother liftUpInfo={catchInfo}/>
        <button className={'btn btn-primary'}
        onClick={clicked.bind(null,information.current)}>change</button>
        </div>
    )
}

export default Any
Enter fullscreen mode Exit fullscreen mode

This is the final result on the screen:

Alt Text

Now we are able to change the state of the innermost component in an event handler defined and handled in the outermost component.

Use of HOC

We can achieve the same result if we make use of a higher-order component. Let's define the next HOC:

import React, { useState,useRef } from 'react'

export default C=>(props)=>{
    const [foo,setFoo]=useState(false)

    const information=useRef(null)

    const catchInfo=(info)=>{
        information.current=info
        setFoo(prev=>!prev)
    }

    return (
        <C catchInfo={catchInfo} information={information} {...props}/>
    )
}
Enter fullscreen mode Exit fullscreen mode

As you can see we are generating the catchInfo and information props and passing them down to the component, with the rest of the props.

Let's show an example of the use of this HOC with the rest of the components redefined (except for Some component, which remains the same):

import React,{useEffect} from 'react'
import * as styles from './index.module.css'
import withLiftUp from '../../hocs/withLiftUp'
import Some from '../Some'

const Other2Bis=({catchInfo,information,liftUpInfo})=>{

    useEffect(()=>{
        liftUpInfo.bind(null,information.current)()
    },[])

    return (
        <div className={styles.general}>
            <Some liftUpInfo={catchInfo}/>
        </div>
    )
}

export default withLiftUp(Other2Bis)
Enter fullscreen mode Exit fullscreen mode

And the rest of the components redefined are:

import React,{useEffect} from 'react'
import * as styles from './index.module.css'
import withLiftUp from '../../hocs/withLiftUp'
import Other2Bis from '../Other2Bis'

const YetAnotherBis=({catchInfo,information,liftUpInfo})=>{

    useEffect(()=>{
        liftUpInfo.bind(null,information.current)()
    },[])

    return (
        <div className={styles.general}>
            <Other2Bis liftUpInfo={catchInfo}/>
        </div>
    )
}

export default withLiftUp(YetAnotherBis)
Enter fullscreen mode Exit fullscreen mode

which is exactly the same as the previous one, and finally:

import React from 'react'
import * as styles from './index.module.css'
import withLiftUp from '../../hocs/withLiftUp'
import YetAnotherBis from '../YetAnotherBis'

const AnyBis=({catchInfo,information})=>{

    const clicked=(setValue)=>{
        setValue(prev=>!prev)
    }

    return (
        <div className={styles.general}>
            <YetAnotherBis liftUpInfo={catchInfo}/>
            <button className={'btn btn-primary'}
            onClick={clicked.bind(null,information.current)}>change</button>
        </div>
    )
}

export default withLiftUp(AnyBis)
Enter fullscreen mode Exit fullscreen mode

With this, we get the same result as before.

Top comments (0)