DEV Community

John Au-Yeung
John Au-Yeung

Posted on • Originally published at thewebdev.info

React Tips — Modern Structures and State Updates

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

React is the most used front end library for building modern, interactive front end web apps. It can also be used to build mobile apps. In this article, we’ll look at some tips and tricks to make building apps with React easier.

Reduce the Use of Class Components

Class components have issues like dealing with lifecycle hooks and making sure the value of this is the correct one.

With the introduction of React hooks, function components are now smart. Therefore, we can use function components like we did we with class components, but without the headache of this and lifecycle hooks.

For instance, we can easily create a component that loads something from an API with the useEffect hook as follows:

import React from "react";

export default function App() {
  const [name, setName] = React.useState({});
  const getName = async () => {
    const res = await fetch("https://api.agify.io?name=michael");
    setName(await res.json());
  };
  React.useEffect(() => getName(), []);
  return <div className="App">{name.name}</div>;
}
Enter fullscreen mode Exit fullscreen mode

In the code above, we have the React.useEffect call to call an API to get some data on load. We set the data with the setName function that’s returned from React.useState , which returns a state variable and a function to set the state.

Then we display it in the div that we returned.

To do the same thing with class-based components, we’ve to write the following:

import React from "react";

export default class App extends React.Component {
  constructor() {
    super();
    this.state = {};
  }

  async componentDidMount() {
    const res = await fetch("https://api.agify.io?name=michael");
    this.setState({ name: await res.json() });
  }

  render() {
    return <div className="App">{this.state.name.name}</div>;
  }
}
Enter fullscreen mode Exit fullscreen mode

As we can see, it’s a bit longer. Also, we have to extend the React.Component class to create a class component.

Also, we have to initialize this.state in the constructor and also call super .

In the componentDidMount hook, which is the same as using useEffect with an empty array as the second argument, we call the code to load the data that we want from the API.

Then we render the data in the render method with the div as we did before, except that we’ve to reference this.state to do that.

As we can see, the function component version is shorter. Also, we can use hooks however we see fit, unlike component lifecycle methods, which only run in certain parts of the lifecycle.

For instance, we can change the useEffect hook to watch for input value changes and call the API as follows:

import React from "react";

export default function App() {
  const [name, setName] = React.useState("");
  const [result, setResult] = React.useState({});
  const getName = async () => {
    const res = await fetch(`https://api.agify.io?name=${name}`);
    setResult(await res.json());
  };
  React.useEffect(() => {
    getName();
    return () => {};
  }, [name]);

  return (
    <div className="App">
      <input onChange={e => setName(e.target.value)} />
      <p>{result.name}</p>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

In the code above, we used the useEffect hook to watch for the change of the value of name by passing name into the array of useEffect as the second argument.

The return statement in the useEffect callback is for running any cleanup code if needed.

We then call getName in a similar way as before, except that we interpolated name in the URL. As we can see, the useEffect hook does a lot more than a single lifecycle method in React can do. We did all that without having to reference this , which is also great since it’s always confusing.

This is one more reason to use function components with hooks.

Don’t use Props in Initial State

We shouldn’t use props in the initial state because the constructor is called only once when the component is created.

This means that when we make some changes to props next time, the component state won’t be updated.

The better way to reference props is to do it in componentDidUpdate . It’s a lifecycle method that lets us update the component when something change, such as when props change.

For instance, we can call this.setState in componentDidUpdate as follows:

import React from "react";

class Count extends React.Component {
  constructor() {
    super();
    this.state = {
      count: 0
    };
  }
  componentDidUpdate(prevProps) {
    if (this.props.count !== prevProps.count) {
      this.setState({ count: this.props.count * 2 });
    }
  }
  render() {
    return <div>{this.state.count}</div>;
  }
}

export default function App() {
  const [count, setCount] = React.useState(0);
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <Count count={count} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

In the code above, we check if prevProps.count ‘s value is different from this.props.count ‘s the value before calling this.setState so that we don’t call it repeatedly and unstoppably. Then we render this.state.count that we set in the render method.

This is the right because we’re updating the state according to the value of the prop. We should get the count displaying the count prop passed in App multiplied by 2.

If it’s in the constructor, then this.state.count won’t update because it won’t update when the props change as we have in our example.

Conclusion

With the introduction of hooks, it’s time to embrace function components and hooks together to create stateful components. It reduces the headaches with dealing with lifecycle methods and the value of this .

Also, if we’re using class components, then we have to remember not to set props as the value of a state property in the constructor, because the prop will never update the state after the first time the component loads. Instead, we should call setState in componentDidUpdate to update the state with our prop value.

Top comments (0)