Passing Data From Child to Parent Component in TypeScript React

By Ndukwe Armstrong

May 1st, 2021

image

In modern-day front-end development due to its complexity and size, there’s a need for robust libraries, tools, and frameworks. With React, we are able to effectively manage complex states, animations, frequent updates, and DOM manipulations. TypeScript on the other has been a huge bridge to overcome most of the shortcoming of JavaScript such as been loosely typed.

In this article, I will be introducing to you how to pass data from a child component to a parent component in React using TypeScript, so grabs a cup of coffee and read on.

Data Flow In React

data flow | react

data flow | react

Basically React is designed to have a unidirectional data flow, this concept means that data has one, and only one, way to be transferred to other parts of the application i.e state is passed to the view and to child components.

below is a simple code snippet that shows the data flow

// parent.tsx

const Parent:React.FC = () => {

const [parentName, setParentName] = useState<string>('John Obi')

return (
     <div>

       <FirstChild name={parentName} />
       <SecondChild name={parentName} />

     </div>

  )
}

This is one of the reasons states are usually moved to the top of the component tree so that they can be shared between components that need to access it.

Also, it's worthy to note that, with this general unidirectional data flow of react applications, a change in the state of a child component does not affect the parent but only the child and components that are children to that child if it has.

I will be introducing a basic way this shortcoming is been overcome, there are other ways, one of which is using a global store such as redux and the context API.

Overcoming This Shortcoming

firstly, let's define our children components of the parent component seen above

// firstChild.tsx

interface IfirstChildProps {
  name: string

}

const FirstChild: React.FC<IfirstChildProps> = ({name}) => {

const [firstChildName, setFirstChildName] = useState<string>('')

return (

<section>
  <h1> {firstChildName} </h1>
 <button> first child </button>
</section>

)

}

export default FirstChild;

In TypeScript, you have to define types for everything, therefore, for our firstChild component, we declared her props to be of type IfirstChildProps and declared the name prop to be a string. we also gave it a local state of firstChildName, in the future, we will aim at making the local state value to be the state of her parent component.

Remember by default in react, you can only pass data or state in one direction i.e from parent to child component

now let’s declare the secondChild component in the same manner

// secondChild.tsx

interface IsecondChildProps {
  name: string

}

const SecondChild: React.FC<IsecondChildProps> = ({name}) => {

const [secondChildName, setSecondChildName] = useState<string>('')

return (
<section>


  <h1> {secondChildName} </h1>
  <button>second child</button>

</section>

)

}

export default SecondChild;

This just renders the buttons from the children components and an empy h2 html tag.

So at this point, we will add the useEffect hook to each child component, which sets the parentName prop(from the parent) as the value of the local state names(FirstChildName, SecondChildName) of her children.

// firstChild.tsx

interface IfirstChildProps {
  name: string

}

const FirstChild: React.FC<IfirstChildProps> = ({name}) => {

const [firstChildName, setFirstChildName] = useState<string>('')

useEffect(() => {

  setFirstChildName(name)

},[name])

 ...

export default FirstChild;

pls note that the name prop is added to the dependency of the useEffect hook, with this, once the component mounts and anytime the name prop changes it will re-render the components

let’s do the same for the SecondChild component

// secondChild.tsx

interface IsecondChildProps {
  name: string

}

const SecondChild: React.FC<IsecondChildProps> = ({name}) => {

const [secondChildName, setSecondChildName] = useState<string>('')

useEffect(() => {

setSecondChildName(name)

},[name])

 ...

export default SecondChild;

Now, we can see the name “John Obi” from the parent component, rendering on both components

Implementation

At this point, we will implement our purpose — passing data from the child to the parent component

let’s begin this by adding a helper function to the parent component. let’s name this function updateName, this function will accept an argument of type string and update the state of the parent component when called.

// parent.tsx

const Parent:React.FC = () => {

const [parentName, setParentName] = useState<string>('Mr John Obi');

const updateName = (name: string):void => {
     setParentName(name)
}

return (
     <div>

      <FirstChild name={parentName} />
      <SecondChild name={parentName} />

    </div>

)
}

Alright, I think that was simple

Next, We pass this function as a prop to the children's components

// parent.tsx

const Parent:React.FC = () => {

const [parentName, setParentName] = useState<string>('Mr John Obi');

const updateName = (name: string):void => {
     setParentName(name)
}

return (
     <div>

       <FirstChild name={parentName} updateName={updateName} />
       <SecondChild name={parentName} updateName={updateName} />

    </div>

)
}

Woo! TypeScript should be yelling at you at this point for passing a prop not part of the prop type(interface) for the components, don't worry we will fix it in the next few paragraphs.

We will simply add this updateName prop as part of the type definition(interface) of the props for each of the children

// firstChild.tsx

...

interface IfirstChildProps {
  name: string,
  updateName: (arg: string) => void

}

const firstChild: React.FC<IfirstChildProps> = ({name, updateName}) => {

 ...

export default firstChild;

Also, do the same for the SecondChild component

// secondChild.tsx

...

interface IsecondChildProps {
  name: string,
  updateName: (arg: string) => void
}

const SecondChild: React.FC<IsecondChildProps> = ({name, updateName}) => {

...

export default SecondChild;

I think we have come a long way, at this point we would add an onclick event to the button of each child, so that when clicked the updateName function will be called with a given string

let’s call our FirstChild component Micheal, and our secondChild Component David, so when the button in each of the children component is clicked, it shows the names, Micheal or David.

shall we?

...

return (

  <h1> {firstChildName} </h1>
  <button onClick={() => updateName('Micheal')}>first child</button>

    )

}

export default firstChild;

do the same for the SecondChild component

...

return (

  <h2> {secondChildName} </h2>
  <button onClick={() => updateName('David')}>second child</button>

)

}

export default secondChild;

Congratulations!, you just passed data to the parent from a child

Wow! so easy right? try clicking the buttons and see the names updating to either David or Micheal

result when on the first-child button is clicked

result when on the first-child button is clicked

result when the second-child button is clicked

result when the second-child button is clicked

Conclusion

In general, React has a unidirectional data flow, i.e data is designed to flow in one direction — parent to children. But the basic strategy use to pass data the other way round is passing and calling a function with the required data from the parent to the child component.

Hope you learned cool today, Thanks for reading this article, you can get the full code here and also follow me on Twitter, Github, and Linkedin, have a lovely day.



Continue Learning