How To Build Great React Components Easily And Effortlessly | Hacker Noon

Edem Agbenyo Hacker Noon profile picture

@edemagbenyoEdem Agbenyo

Passionate software developer with over 5 years of experience. Sedem’s father.

React is such a powerful library, that everyone with knowledge of the basics can build a really good application. Managing state in react is built out of the box with React own state management APIs.

But as your app gets more complex, it becomes harder to track, get a better hold of your state and understand what is going on. In order to improve the understanding of your code at such a moment, React has made available techniques and APIs that helps us build components that seamlessly work.

Some of those techniques and API are:

  • HOC(Higher Order Component)
  • Render Props
  • React Context

HOC(Higher Order Component)

HOC is an advanced technique to React to reusing component logic. Just like a Higher Order Function, which receives a function as an argument and returns a function, a HOC takes a component as an argument and returns a new component.

Let’s take this code for example:

import React from 'react'

function Students() {
  const students = [
    { name: "John", score: "A-" },
    { name: "Samuel", score: "B-" },
    { name: "Smith", score: "A+" },
    { name: "Mark", score: "A-" },
    { name: "Mike", score: "B-" },
    { name: "John", score: "B+" },
  ];

  return (
    <div>
      {students.map((student) => (
        <p>
          {student.name} - {student.score}
        </p>
      ))}
    </div>
  );
}

From the snippet of code above, we could tell that the list of students and their grade is tied to the

Students

component. What happens when another component needs to make use of that same list? We do not want to copy and paste the same list across all components. But what we want is a reusable component that could be used by other components. This is where HOC shines, it allows us to create a Wrapper Component that provides other components with the data they need.

import React from "react"

function Students(props) {
  return (
    <div>
      {props.students.map((student) => (
        <p>
          {student.name} - {student.score}
        </p>
      ))}
    </div>
  );
}
const withStudents = (Component) => {
  const students = [
    { name: "John", score: "A-" },
    { name: "Samuel", score: "B-" },
    { name: "Smith", score: "A+" },
    { name: "Mark", score: "A-" },
    { name: "Mike", score: "B-" },
    { name: "John", score: "B+" },
  ];
  return () => <Component {...students}></Component>;
};
const ComponentWithStudents = withStudents(Students);
export default ComponentWithStudents;

We create a

withStudents

component which accepts any component as argument and supplies data to it in the form of

props

. The wrapper component

withStudents

returns the supplied component by wrapping it in a container component, it does not alter the argument component in any way. HOC are pure functions with no side-effects. The syntax above will look familiar to you if you have worked with redux before.

We could pass extra parameters to our wrapper component by doing the following:

const withStudents = (count) => (Component) => {
  const students = [
    { name: "John", score: "A-" },
    { name: "Samuel", score: "B-" },
    { name: "Smith", score: "A+" },
    { name: "Mark", score: "A-" },
    { name: "Mike", score: "B-" },
    { name: "John", score: "B+" },
  ];
  const listStudentsLimited = students.slice(0, count);
  return () => <Component students={listStudentsLimited}></Component>;
};
const maxStudentCount = 3;
export default withStudents(maxStudentCount)(App);

Our

Students

component remains the same while the

withStudents 

wrapper now returns a function that wraps what was previously returned, making it a true Higher Order Function :).

Next, we will look at how we could use Render Props to do similar data sharing.

Render Props

The second way by which we can share data among components is with Render Props. From the react.js , it defines render props to be

A component with a render prop takes a function that returns a React element and calls it instead of implementing its own render logic.

So using our previous example, we create a render props component that surrounds the rendering part of the original

Student

component. The Render Props in turn returns the component as its child and passes any data to it.

import React from "react";
function Students() {
  return (
    <StudentWithRenderProps>
      {({ students }) => (
        <div>
          <h1>Students with grades</h1>
          {students.map((student) => (
            <p>
              {student.name} - {student.score}
            </p>
          ))}
        </div>
      )}
    </StudentWithRenderProps>
  );
}
const StudentWithRenderProps = (props) => {
  const students = [
    { name: "John", score: "A-" },
    { name: "Samuel", score: "B-" },
    { name: "Smith", score: "A+" },
    { name: "Mark", score: "A-" },
    { name: "Mike", score: "B-" },
    { name: "John", score: "B+" },
  ];
  return props.children({
    students,
  });
};
export default Students;

Context

From the React.js website, it defines context as follow,

Context provides a way to pass data through the component tree without having to pass props down manually at every level.

This is one way to solve the component drilling issue, where you have to pass data through several components to share data with children located down in the components. Using Context makes it easier to share data among many components within an application; data such as user session, theme or language.

In our example, we are going to use a context to share user-session information among components that need it.

export const AuthContext = React.createContext({});

export default function App() {
  const userInfo = {
    name: "John Smith",
    email: "[email protected]"
  };
  return (
    <AuthContext.Provider value={userInfo}>
      <Profile></Profile>
    </AuthContext.Provider>
  );
}

First, we create the context

React.createContext({})

and assign it to a variable. This will be used to wrap any consuming component with the help of the Provider component that is made available by the context. The Provider accepts a

value

prop that contains the data to share among any nested components. In our case, we want to share the

userInfo

.

Next, for any component to access the data being shared by a context, we need to get a reference of the context and pass it to the

useContext

hook made available by React.

import { useContext } from "react";
import { AuthContext } from "./App";
export default function Profile() {
  const auth = useContext(AuthContext);
  console.log(auth);
  return (
    <div>
      User is
      <span style={{ color: "red" }}>
        {Object.keys(auth).length > 0 ? "Logged in" : "Logged out"}
      </span>
    </div>
  );
}

Now, the Profile component has access to the

userInfo

from the AuthContext.

Both HOC and Render Props works almost in the same way. HOC works in a way that feels like it works behind the scene, while Render Props are more frontend centered. They are less code-intensive as compared to Context. Context on the other hand allows us to give all consuming components access to the data passed to the

Provider

.

Also published at https://dev.to/edemagbenyo/build-better-components-with-react-3kha/edit

Edem Agbenyo Hacker Noon profile picture

Read my stories

Passionate software developer with over 5 years of experience. Sedem’s father.

Tags

Join Hacker Noon

Create your free account to unlock your custom reading experience.

read original article here