React

What is Component nesting in React?

In this article, we will learn about the nesting of components. We will also learn about some of the scenarios which lead...

Written by Shivangi Rajde · 5 min read >
Component nesting in React

In this article, we will learn about the nesting of components. We will also learn about some of the scenarios which lead to the nesting of components rather than writing static code. We will also learn about reusing our components so that we can use the component throughout our application. Reusing components would be a best practice to reduce redundancy in our code and make it more readable.

Let’s start by taking a simple example of a very basic react application. Our react application has a list of posts rendered from the API which is a free fake API for testing and prototyping.

You might be wondering that why do we require the concept of nesting the components. To make an actual react application work we need to have multiple components related to each other or might be communicating to with each other.

Here, let’s take an example. We have a list of posts that displays the heading of the post, details of the post, and an edit button to edit the details and heading of the post. As you can see in the image below that there are various posts displayed and you might be finding some of the things common.

Here one thing that we can do is rather than repeating the things we can reuse the same piece of code for the information that functions and looks similar. As we can see in the image that the list of posts has much repetitive code so why not reuse that code?

Demo

Let’s start by looking into the code that has nesting implemented in it. You can visit the link provided above so that you can understand code in a much better way.

When creating a new project there won’t be any relation between the index.js file and other components that we would create. To show the details of our component, we need to add our components in the index.js file.

How to create relation between components?

First, we need to understand the relations between our components. Index.js file has the default component of which the data is being viewed when created.

import React, { Component } from 'react'; 
import { render } from 'react-dom'; 
import Posts from './Components/Posts'; 
import './Components/style.scss'; 

class App extends Component { 
  render() { 
    return ( 
      <div>         
        <Posts /> 
      </div> 
    ); 
  } 
} 
render(<App />, document.getElementById('root')); 

Index.js

To show up the list of posts and the form to add/edit the posts we need to make our components that contain the form and the list of posts available to other components in the projects. To make the component available to other components in our project we need to add the line “export default Posts” at the end of the component.

The export default line makes the component available to every other component in the project. Now we need to use the Posts component in our index.js file so that we can have the content of the Posts component connected to the index.js file.

Let’s have a look at our Post.js file which has the

posts.js

To use the Posts component in our index.js file we need to import the component inside the component in which we are gonna make it available to use in our case we need to import it inside the index.js file.

App.js

Syntax use to import the components in ES6 is as shown below:

Import componentName from ‘path of the component’

We imported our Posts component using the statement

import Posts from './Components/Posts'; 

To access this component, we need to use the name of the component by which we have imported I.e., “Posts”. By using the import statement, we can access the Posts component in the index.js file, in this way we can create a relation between the components. So basically, this concept is known as component nesting in react.

Let’s have a look at what react docs state about component nesting’s idea: Props and composition give you all the flexibility you need to customize a component’s look and behavior in an explicit and safe way. Remember that components may accept arbitrary props, including primitive values, React elements, or functions.

If you want to reuse non-UI functionality between components, we suggest extracting it into a separate JavaScript module. The components may import it and use that function, object, or class, without extending it.

We are using multi-level children, the hierarchy that we have followed is as shown in the image below:

Component hierarchy

Our root component is the index.js component in which we have imported our Posts.js component consisting of PostForm.js and PostList.js components. The Posts component consists of all the API calls and business logic required for child components for example fetching the posts from the API, operation to be performed on creating a post, and updating any of the posts.

import React, { Fragment, useState, useEffect } from "react";
import PostForm from "./PostForm";
import PostList from "./PostList";
import axios from "axios";

const Posts = () => {
  const [allPosts, setallPosts] = useState([]);
  const [isFormEditing, setEditing] = useState(false);
  const [editData, setEditData] = useState({});

  useEffect(() => {
    function posts() {
      axios.get("https://jsonplaceholder.typicode.com/posts").then(response => {
        setallPosts(response.data);
      });
    }
    posts();
  }, []);

  const getPost = postId => {
    // console.log("GET POST", isFormEditing);
    setEditing(true);
    axios
      .get("https://jsonplaceholder.typicode.com/posts/" + postId)
      .then(res => {
        console.log("GET POST RES: ", res.data);
        setEditData(res.data);
      });
  };

  const onPostCreate = formValues => {
    const addPost = allPosts;
    console.log("addpost: ", addPost);
    addPost.unshift(formValues);
    axios
      .post("https://jsonplaceholder.typicode.com/posts", formValues)
      .then(res => {
        // console.log("RES", res);
        setallPosts(addPost);
      });
  };

  const onPostUpdate = formValues => {
    // console.log("onPostUpdate called: ")
    var a = allPosts.findIndex(i => i.id == formValues.id);
    allPosts[formValues.id] = formValues;
    axios
      .put("https://jsonplaceholder.typicode.com/posts/" + formValues.id, {
        formValues
      })
      .then(res => {
        setallPosts(res.data);
      });
  };

  return (
    <Fragment>
      <h2>Posts Component</h2>
      <PostForm
        onUpdateClick={onPostUpdate}
        onAddClick={onPostCreate}
        editMode={isFormEditing}
        editingPost={editData}
      />
      <hr />
      <PostList
        list={allPosts}
        onEditClick={getPost}
        editMode={isFormEditing}
      />
    </Fragment>
  );
};
export default Posts;

Posts.js

This is the code of the PostForm component that has form fields for adding/editing any of the selected posts.

import React, { Fragment, useState, useEffect } from "react";

const PostForm = ({ onAddClick, onUpdateClick, editMode, editingPost }) => {
  const [formValues, setFormValues] = useState({
    title: "",
    body: ""
  });

  useEffect(() => {
    function getEditValues() {
      console.log("EDITING", editMode);
      if (editMode) {
        console.log("EDITING POST: ", editingPost);
        setFormValues({
          title: editingPost.title,
          body: editingPost.body,
          id: editingPost.id
        });
      }
    }
    getEditValues();
    // }) Leads to infinite loop
  }, [editingPost]);

  const onInputChange = e => {
    setFormValues({
      ...formValues,
      [e.target.name]: e.target.value
    });
  };

  const onSubmit = e => {
    // console.log("FORM VALUES: ", formValues);
    e.preventDefault();
    if (editMode) {
      onUpdateClick(formValues);
    } else {
      onAddClick(formValues);
      console.log("Submitted");
    }
    //TODO: form doesn't clear
    setFormValues({ title: "", body: "", id: "" });
  };

  return (
    <Fragment>
      <div className="post-form">
        <h3>Post Form Component</h3>
        <form>
          <label>Title:</label>
          <br />
          <input
            value={formValues.title}
            type="text"
            name="title"
            onChange={onInputChange}
          />
          <br />
          <label>Body:</label>
          <br />
          <textarea
            style={{ width: "400px" }}
            value={formValues.body}
            name="body"
            rows={5}
            onChange={onInputChange}
          />
          <br />
          <button onClick={onSubmit}>
            {editMode ? "Update Post" : "Add to list"}
          </button>
        </form>
      </div>
    </Fragment>
  );
};
export default PostForm;

PostForm.js

This is the PostList component that is used to display the posts fetched using the get request. The fragment gets repeated that contains the heading, body, and button for editing the component.

import React, { Fragment } from "react";

const PostList = ({ list, onEditClick }) => {
  const postItems = () => {
    // console.log("post List called");
    if (list) {
      console.log("LIST: ", list);
      return list.map((item, index) => {
        // console.log(item);
        return (
          <div key={index} className="individualPost">
            <h3>
              {index + 1}) {item.title}
            </h3>
            <p>{item.body}</p>
            <button onClick={() => onEditClick(item.id)}>Edit</button>
          </div>
        );
      });
    }
  };

  return (
    <Fragment>
      <div className="post-list">
        <h3>Post List Component</h3>
        <div>{postItems()}</div>
      </div>
    </Fragment>
  );
};
export default PostList;

PostList.js

Pros of using component nesting

  • Easy life cycle management for components
  • Better reusability
  • Good composability

Cons of using component nesting

  • Managing props and passing props to multi-level child components can be expensive

Summary

In this article we learnt about various terms such as:

Export: Term used to make the component available for other components in the project.

Import: Term used to access a specific component inside another existing component. It also allows us to use multiple objects from a file by using a tree-shaking mechanism.

We also have an idea about how to nest components at a multi-level and where can we reuse the components.

Loading

Leave a Reply

Your email address will not be published. Required fields are marked *