Advanced React Design Patterns

Rohit Rai

Senior Software Engineer @ Red Hat
Github: rohitkrai03
Twitter: rohitkrai03

What are Design Patterns?

Design patterns are typical solutions to common problems in software design. Each pattern is like a blueprint that you can customize to solve a particular design problem in your code.

React design patterns are used to simplify large React applications and helps your team to build separate components and share logic between them.

Design Patterns are the recipes that help us cook delicious React Apps.

Higher Order Components (HOC)

A higher-order component (HOC) is a function that takes a component and returns a new component.

  • HOC is not part of the React API.
  • It is a pattern emerged from React’s compositional nature for reusing component logic.
  • A normal component transforms its’ props into UI, but a HOC transforms a component into another component.
  • For example, redux use this to merge props from store from top level to any component.

A Higher Order Component is just a React Component that wraps another one.

The “wraps” part of the definition is intentionally vague because it can mean one of two things:

  • The HOC manipulates the props being passed to the WrappedComponent,
  • The HOC extends the WrappedComponent.

Basic Example

const withUpperCaseUsername = (WrappedComponent) => (props) => {
  return (
    <div>
      <WrappedComponent {...props}>
        {props.children.toUpperCase()}
      </WrappedComponent>
    </div>
  )
}

const Username = (props) => (
  <div>{props.children}</div>
)

const UpperCaseUsername = withUpperCaseUsername(Username);

const App = () => (
  <div>
    <UpperCaseUsername>Rohit Rai</UpperCaseUsername> //ROHIT RAI
  </div>
);

A Real Life Example

// This might come from a config file for example
const featureToggles = {
  helloWorld: true
};

const withFeatureToggle = (feature, WrappedComponent) => {
  return class extends React.Component {
    render() {
      if (!featureToggles[feature]) {
        return null;
      } else {
        return <WrappedComponent {...this.props} />;
      }
    }
  };
}

const HelloWorld = () => <h1>Hello world</h1>;


const HelloWorldWithToggle = withFeatureToggle("helloWorld", HelloWorld);

render(<HelloWorldWithToggle />, document.getElementById("root"));

Render Props

The term “render prop” refers to a technique for sharing code between React components using a prop whose value is a function.

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

A render prop is a function prop that a component uses to know what to render.

Understanding Render Props at a Conceptual Level

const sum = (a, b) => {
    const result = a + b;
    console.log(result);
}
const sum = (a, b, fn) => {
    const result = a + b;
    fn(result);
}

// Usage

sum(1, 2, (result) => {
    console.log(result);
});


sum(3, 5, (result) => {
    alert(result);
});

Simple Example

const HelloWorld = (props) => props.render('Rohit Rai');


<HelloWorld render={(data) => <h1>My Name is {data}</h1>} />

A Real World Example

class ResourceList extends React.Component {
  state = { list: [] };

  fetchData() {
    axios.get(this.props.link)
      .then((response) => {
        this.setState({
          list: response.data,
        });
    })
  }

  componentDidMount() {
    this.fetchData();
  }

  render() {
    return this.props.render(this.state);
  }
}
class RandomResources extends React.Component {
  render() {
    return (
      <ResourceList
        link="https://jsonplaceholder.typicode.com/users"
        render={({ list }) => (
          <div>
            <h2>Random Resources</h2>
            <ul>{list.map(resource => <li key={resource.id}>{resource.name}</li>)}</ul>
          </div>
        )}
      />
    );
  }
}

A Real World Example - Cont.

HOC vs Render Props

  • Indirection - It is tricky to tell which HOC provides which props.

  • Naming collisions - Two HOCs that try to use the same prop name will collide and overwrite one another.

  • Ensure all relevant props are passed through to the component.

  • It is easy to compose several HOCs together and then this creates a deeply nested tree making it difficult to debug.

  • Indirection. We don’t have to wonder where our state or props are coming from. We can see them in the render prop’s argument list.

  • Naming collisions. There is no automatic merging of property names, so there is no chance for a naming collision.

  • Caution using shouldComponentUpdate as the render prop might close over data it is unaware of.

Hooks

Hooks are functions that lets you "hook" into React internal state and lifecycle features from a function component.

  • New React API
  • Function Components
  • Future of React

How do we reuse our code in react ?

  • Write simple functions and call them to calculate something.
  • Write components (which themselves could be functions or classes).

Why Hooks?

Component has State?

Class Component

Function Component

YES

NO

 

Can't break down complex components with stateful logic into functions or reusable components.

 

Common use cases include animations, form handling, connecting to external data sources.

  • Huge components that are hard to refactor and test.
  • Duplicated logic between different components and lifecycle methods.
  • Complex patterns like render props and higher-order components.
  • Hooks let us organize the logic inside a component into reusable isolated units.
  • Hooks will reduce the number of concepts you need to juggle when writing React applications.
  • Hooks let you always use functions instead of having to constantly switch between functions, classes, higher-order components, and render props.

How Hooks solve these problems?

Show me some code!

useState

const [count, setCount] = useState(0);

useState, as the name describes, is a hook that allows you to use state in your function.

import React, { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
// Declare multiple state variables!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);

useEffect

  • componentDidUpdate
  • componentDidMount
  • componentWillUnmount
const [data, setData] = useState();

useEffect(() => {
  const fetchGithubData = async (name) => {
    const result = await axios(`https://api.github.com/users/${name}/events`)
    setData(result.data)
  }
  fetchGithubData('rohitkrai03')
}, [data])
const [count, setCount] = useState(0);

useEffect(() => {
  document.title = `You clicked ${count} times`;
});

Custom Hooks

You can combine built-in Hooks provided by React into your own “custom Hooks”.

const ResponsiveComponent = () => {
  const width = useWindowWidth();
  
  return (
    <p>Window width is {width}</p>
  )
}
const useWindowWidth = () => {
  const [width, setWidth] = useState(window.innerWidth);
  
  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handleResize);
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  });
  
  return width;
}

Rules of Hooks

  • Only Call Hooks at the Top Level

    • Don’t call Hooks inside loops, conditions, or nested functions.

  • Only Call Hooks from React Functions

    • Don’t call Hooks from regular JavaScript functions.

Thank You!

Made with Slides.com