DEV Community

Cover image for Manipulating Children dynamically in React
Srishti Prasad
Srishti Prasad

Posted on

Manipulating Children dynamically in React

Learning how to add functionality dynamically to children in React enhances your ability to build flexible, reusable, and customizable components. It promotes good software design 🖥️principles such as composition, reusability, and extensibility, making your code more maintainable and adaptable to changing requirements.
Grab a cup of coffee ☕ and get ready to learn one of the most important concept with me 🤓

We all need to be aware that when utilising a react unique prop in read-only mode, your components will take it exactly as it is given. What if I told you, however, that you could go much further and alter the components of your children in any way, shape, or form?
Let's learn how they can be used to manipulate the Render Output dynamically.
The powerful react APIs are -

React.cloneElement,React.children, and Render props

1. React.cloneElement()

Syntax

React.cloneElement(element,[props],[...children])
Enter fullscreen mode Exit fullscreen mode

It is part of the React top-level API and it's used to manipulate and transform elements. You can either import react as a global object and the top of your file and access them as methods on that object. Or alternatively as a named import.

//As global object
React.cloneElement(element,[props],[...children])
Enter fullscreen mode Exit fullscreen mode

OR

import {cloneElement} from "react"
//To use it
cloneElement(element,[props],[...children])
Enter fullscreen mode Exit fullscreen mode

React.cloneElement effectively clones and returns a new copy of a provided element.

  • The first argument is the react element you would like to clone,
  • the second argument is the prompts that will be added and merged with the original props passed into the component.
  • the third argument is the children of the cloned object. Note that the children of the existing object are not copied

Remember that prompts in react are immutable objects. You must create a copy of the element first and perform the transformation in the copy.That's exactly what React.cloneElement allows you to achieve.

For example you could add another prop dynamically to the submit button element illustrated below.

const buttonElement={
 type:SubmitButton,
 prop:{
   color:"green",
   children:"submit"
 }
};


const output=React.cloneElement(buttonElement,{disable:false})

//Adding another prop dynamically to submit button element 

{
 type: SubmitButton,
 props:{
   color:"green",
   children:"submit",
   disable:false
 },
}

Enter fullscreen mode Exit fullscreen mode

React.cloneElement() is used within the definition of a parent component to perform the following processes:

  1. Modify children properties
    • Repeated characters
    • Fancy child button
    • Modify radio button attributes at a go
    • Clone another React element as a prop
  2. Add to children properties
    • Bold text
    • Pass prop to an element received via React.cloneElement()
  3. Extend the functionality of children components
    • Alert on click

2. React.Children

Another important top-level API useful for children manipulation is react.children, which provides utilities for dealing with the props.children data structure.
NOTE:- props.children is the prop that holds the nested content passed to a component, while React.Children provides utility methods to work with and manipulate the children.

The most important method is the map function. React.children.map is very similar to the map function from arrays and invokes a function in every child contained within its children prop, performing a transformation and returning a new element.
Other methods are

  • React.Children.toArray
  • React.Children.count
  • React.Children.only

let's take example of one of the method of this API:

The API for the RadioGroup component is defined as two props: selected, which is a string that matches one of the RadioOption values and onChange, which is the event that gets called whenever a selection changes, providing the new value as an argument.

Code of App.js

import { RadioGroup, RadioOption } from "./Radio";
import { useState } from "react";
export default function App() {
  const [selected, setSelected] = useState("");
  return (
    <div className="App">
      <h1>How did you hear about our company?</h1>
    <RadioGroup onChange={setSelected} selected={selected}>
      <RadioOption value="Social media">Social media</RadioOption>
      <RadioOption value="friends">friends </RadioOption>
    <RadioOption value="Advertisement">Advertisement</RadioOption>
      <RadioOption value="others">others </RadioOption>
    </RadioGroup>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

The RadioOptions variable should be assigned to the return value of React.Children.map, which will be a new React element. The first argument passed to the map function should be the children prop, and the second is a function that gets invoked in every child contained within the children property. Recall that a children prop is a special prop all React components have and that it presents a special data structure, similar to arrays, where you can perform iterations. However, they are not exactly instances of JavaScript arrays. That’s why to iterate over all siblings you should use the special React.children.map API provided by React.

Inside the map projection function, you should first clone the element using React.cloneElement, passing as first argument the target child element and as a second argument a configuration with all new props. The resulting element will have the original element’s props with the new props merged in.

onChange can be passed to each child (RadioOption) as it is and checked is the property the RadioOption uses to determine if the underlying radio input is selected. Since RadioGroup receives a selected property, which is a string pointing to the value of the option that has been selected, checked will be only true for one of the options at any point in time. This is guaranteed by performing an equality check, comparing the RadioOption value prop with the selected value.

Finally, the RadioGroup component returns the new RadioOptions elements by wrapping them in curly braces.

Code of Radio.js

export const RadioGroup = ({ onChange, selected, children }) => {
  const RadioOption = React.Children.map(children, (child) => {
    return React.cloneElement(child, {
      onChange,
      checked: child.props.value === selected
    });
  });
  return <div className="RadioGroup">{RadioOption}</div>;
};

export const RadioOption = ({ value, checked, onChange, children }) => {
  return (
    <div className="RadioOption ">
      <input
        id={value}
        type="radio"
        name={value}
        value={value}
        checked={checked}
        onChange={(e) => {
          onChange(e.target.value);
        }}
      />
      <label htmlFor={value}>{children}</label>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

The value prop is already provided explicitly inside the App.js component and children represents the label text for the radio input.

You have to connect the props value, checked and onChange correctly. First, both value and checked props should be passed to the radio input as is. Then, you should use the onChange event from the radio input, retrieve the value property from the event target object and pass it to the onChange prop as the argument as seen below. That completes the implementation of the RadioOption component.

export const RadioOption = ({ value, checked, onChange, children }) => { 
 return ( 
   <div className="RadioOption"> 
     <input 
       id={value} 
       type="radio" 
       name={value} 
       value={value} 
       checked={checked} 
       onChange={(e) => { 
         onChange(e.target.value); 
       }} 
     /> 
     <label htmlFor={value}>{children}</label> 
   </div> 
 ); 
}; 
Enter fullscreen mode Exit fullscreen mode

Codesandbox Link for demonstrating above code.

Output -

Image description

3. Render props

The render prop pattern in React is often used to dynamically add functionality to child components. The render prop pattern is a technique where a component receives a function as a prop, typically named "render" or "children," which it then calls to render its content. By using this pattern, the parent component can pass additional data or functionality to the child component, allowing for dynamic behaviour.
Here's an example to illustrate how the render prop pattern can be used to add functionality dynamically:

// Parent component
function ParentComponent() {
  return (
    <div>
      {/* Render prop being used */}
      <ChildComponent render={(data) => <p>{data}</p>} />
    </div>
  );
}

// Child component
function ChildComponent({ render }) {
  const data = "Dynamic functionality added!";

  // Calling the render prop function passed from the parent
  return render(data);
}

Enter fullscreen mode Exit fullscreen mode

The ParentComponent renders the ChildComponent and passes a function as the render prop. The ChildComponent calls this function and passes the data as an argument. The returned content from the render prop function is then rendered by the child component.

Hope you get all the concepts very well👐
If you are reading till here 😇, do not forget to hit like💗
Do comment and let me know how did you like the blog.
If struck feel free to reach out to me and post you doubt in comment section, I'll try my best to answer them.

Follow me for more such fundamental driven concepts 👍

thankoyu img

Top comments (6)

Collapse
 
stetrippier profile image
SteTrippier

Bruh. The title looks super sus to anyone who doesn't know what react is. Popped up on my Google news feed with only the title haha

Collapse
 
srishtikprasad profile image
Srishti Prasad • Edited

Haha ! Glad that this sus title landed you to this blog.

Collapse
 
crloz profile image
Under Attack

Good read!
Note that React.cloneElement and React.Children are considered "legacy" APIs
react.dev/reference/react/legacy

Collapse
 
srishtikprasad profile image
Srishti Prasad

Noted✅, thanks for the information

Collapse
 
ra1nbow1 profile image
Matvey Romanov

Pretty nice article. Thanks a lot!

Collapse
 
srishtikprasad profile image
Srishti Prasad

Glad you liked it.