开源日报每天推荐一个 GitHub 优质开源项目和一篇精选英文科技或编程文章原文,坚持阅读《开源日报》,保持每日学习的好习惯。

2024年2月4日,开源日报第1095期:
今日推荐开源项目:《todomvc》
今日推荐英文原文:《UseEffect Hook in React: From Beginner to Pro 📘》


开源项目

今日推荐开源项目:《todomvc》传送门:项目链接

推荐理由: 帮助您选择MV*框架 - 用于React.js,Ember.js,Angular和许多其他框架的Todo应用程序

🔗 链接直达:todomvc.com


英文原文

今日推荐英文原文:UseEffect Hook in React: From Beginner to Pro 📘

推荐理由:深入理解React中的useEffect钩子,从基础到高级的使用方法,如数据获取、订阅和DOM操作, 通过实时示例,展示了useEffect如何模拟类组件中的componentDidMount、componentDidUpdate和componentWillUnmount


UseEffect Hook in React: From Beginner to Pro 📘

React useEffect complete guide

Introduction 🌟

In React, the introduction of Hooks changed how we build components, and useEffect is one of the most important Hooks. It's like the new version of old features from class-based components, but easier to use. useEffect helps us in a better way to handle what happens when a component starts, updates, or ends, in a much simpler way.

In this blog, we will delve into all the details about the useEffect hook, exploring its role in React's functional components and how it simplifies handling component lifecycles.

What is useEffect? 💡

The useEffect hook in React is a powerful tool for handling side effects in functional components. Side effects are any operations that interact with the outside world, like data fetching, subscriptions, or manually altering the DOM. useEffect allows you to perform such actions in a way that is both efficient and organized.

How to Use useEffect 🛠️

The basic syntax of useEffect is straightforward:

useEffect(() => {
  // Your code for the side effect goes here.
  // This code runs after every component render.

  return () => {
    // Optional cleanup code goes here.
    // This part runs when the component unmounts.
  };
}, [dependencies]); // The effect will re-run only if these dependencies change.
  1. Effect Function: The function you pass to useEffect will run after the render is committed to the screen. This makes it perfect for updating the DOM, fetching data, setting up subscriptions, etc.
  2. Dependency Array: The second argument is an array of dependencies. useEffect will only re-run if these dependencies have changed between renders. If you pass an empty array [], the effect runs once after the initial render, mimicking the behavior of componentDidMount in class components.
  3. Cleanup Function: Optionally, your effect function can return a cleanup function. React calls this function before re-running the effect and when the component unmounts. It’s the ideal place for cleanup operations, similar to componentWillUnmount in class components.

The Shift from Class to Functional Components 🔁

In React’s earlier days, class-based components were the standard for managing complex states and lifecycle events. These components utilized specific lifecycle methods to execute code at different stages of a component’s life. Let’s look at a common example:

Lifecycle Methods in a Class Component

Class-based components in React use lifecycle methods for various stages of a component’s life. Here’s a common pattern using lifecycle methods:

class ExampleClassComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = { count: 0 };
    }

    componentDidMount() {
        document.title = `Clicked ${this.state.count} times`;
    }

    componentDidUpdate() {
        document.title = `Clicked ${this.state.count} times`;
    }

    componentWillUnmount() {
        alert('Component is being removed');
    }

    render() {
        return (
            <div>
                <p>You clicked {this.state.count} times</p>
                <button onClick={() => this.setState({ count: this.state.count + 1 })}>
                    Click me
                </button>
            </div>
        );
    }
}

In this example, componentDidMount is used for actions after the component is inserted into the DOM, componentDidUpdate handles changes in state or props, and componentWillUnmount is for cleanup before the component is removed from the DOM.

Functional Components: Introducing Hooks

With the introduction of Hooks, these lifecycle methods can now be handled in functional components, which are generally more concise and easier to read and test.

Rewriting with useEffect in a Functional Component

import React, { useState, useEffect } from 'react';

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

    useEffect(() => {
        // Runs after every render (mounting and updating)
        document.title = `Clicked ${count} times`;

        // Cleanup code (for unmounting)
        return () => {
            alert('Component is being removed');
        };
    }, [count]); // Only re-run the effect if count changes

    return (
        <div>
            <p>You clicked {count} times</p>
            <button onClick={() => setCount(count + 1)}>
                Click me
            </button>
        </div>
    );
}

In the functional component, useEffect is used to handle side effects. The first argument is a function that runs after every render. It replaces both componentDidMount and componentDidUpdate, as it can be configured to run only when specific values (like count) change. The return function inside useEffect is the cleanup mechanism, akin to componentWillUnmount in class components.

Real-Time Examples: Understanding useEffect in Action 🚀

In this section of our blog, we’ll explore three distinct real-time examples to illustrate how the useEffect hook in React functions as an equivalent to the traditional lifecycle methods found in class-based components. These examples will cover data fetching (componentDidMount), responding to state changes (componentDidUpdate), and cleanup operations (componentWillUnmount).

Example 1: Fetching Data on Mount (**componentDidMount**)

The useEffect hook allows us to perform actions when the component first renders, akin to componentDidMount in class components. Let's see this in action with data fetching:

Fetching Data with useEffect

import React, { useState, useEffect } from 'react';

function DataFetcher() {
    const [data, setData] = useState(null);

    useEffect(() => {
        fetch('https://jsonplaceholder.typicode.com/posts/1')
            .then(response => response.json())
            .then(post => setData(post));
    }, []);

    if (!data) {
        return <div>Loading...</div>;
    }

    return <div>Title: {data.title}</div>;
}

This example demonstrates how useEffect can be used for fetching data when the component mounts. We used an empty dependency array to ensure that the effect runs only once, similar to componentDidMount.

Example 2: Responding to State Changes (**componentDidUpdate**)

Next, let’s use useEffect to perform an action in response to a state change, mirroring the functionality of componentDidUpdate.

import React, { useState, useEffect } from 'react';

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

    useEffect(() => {
        document.title = `Count: ${count}`;
    }, [count]);

    return (
        <div>
            <p>Count: {count}</p>
            <button onClick={() => setCount(count + 1)}>Increment</button>
        </div>
    );
}

This Counter component uses useEffect to update the document title every time the count state changes. The dependency on [count] ensures the effect behaves similarly to componentDidUpdate, reacting to changes in specific values.

Example 3: Cleanup Operations (**componentWillUnmount**)

Finally, we’ll look at how useEffect can be used to clean up or perform final actions before the component unmounts, like componentWillUnmount.

import React, { useState, useEffect } from 'react';

function TimerComponent() {
    const [seconds, setSeconds] = useState(0);

    useEffect(() => {
        const intervalId = setInterval(() => {
            setSeconds(prevSeconds => prevSeconds + 1);
        }, 1000);

        return () => clearInterval(intervalId);
    }, []);

    return <div>Timer: {seconds} seconds</div>;
}

The TimerComponent sets up a timer that increments every second. The return function in useEffect is a cleanup function that clears the interval, ensuring no side effects are left when the component unmounts, similar to componentWillUnmount.

These examples show just how easy it is to use the useEffect hook for different parts of a component's life in React. Whether it's fetching data when a component starts, updating things when state changes, or cleaning up when a component goes away, useEffect makes it all straightforward.

Understanding useEffect with Different Dependency Types 🧭

The useEffect hook's behavior can vary significantly with different types of dependencies. Let's dive deeper into how it behaves with objects, null, undefined, and primitive types like strings and numbers.

1. No Dependency Array

If you omit the dependency array, useEffect runs after every render.

useEffect(() => {
    console.log('This runs after every render');
});

Without a dependency array, the effect runs after every render of the component. This is useful when you have an effect that needs to update every time the component updates, regardless of what caused the update.

2. Empty Array []

An empty array means the effect runs once after the initial render, similar to componentDidMount.

useEffect(() => {
    console.log('This runs once, after the initial render');
}, []);

With an empty array, the effect behaves like a componentDidMount lifecycle method in class components. It runs once after the initial render and then never runs again. This is ideal for one-time setup operations.

3. Array with Specific Values

When you include specific values in the array, the effect runs when those values change.

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

useEffect(() => {
    console.log('This runs when "count" changes');
}, [count]);

Here, the effect runs whenever the count state changes. It's a way to make your effect respond specifically to changes in certain data, similar to componentDidUpdate for specific props or state values.

4. Using Objects {} as Dependencies

React’s dependency array doesn’t perform deep comparisons. This means when you use objects as dependencies, React only compares their reference, not their content.

const [user, setUser] = useState({ name: "John", age: 30 });

useEffect(() => {
    console.log('Effect runs if the "user" object reference changes');
}, [user]);

In this snippet, useEffect will re-run only if the user object's reference changes, not its content. For instance, if you update the user's age while keeping the same object reference, the effect won't re-run. This behavior often leads to bugs and is generally not recommended unless you intentionally want to track object reference changes.

5. null and undefined

Using null or undefined as a dependency has a specific effect: the dependency array behaves as if it's not there.

useEffect(() => {
    console.log('This runs after every render, like having no dependency array');
}, null); // or undefined

In this case, the effect runs after every render of the component, which is the default behavior when no dependency array is provided. It’s akin to having an effect without any dependencies listed.

Understanding how different dependency types affect useEffect is crucial for using it effectively. While primitive types like strings and numbers are straightforward and reliable, objects require careful handling due to shallow comparison. Meanwhile, null and undefined effectively remove the dependency check, causing the effect to run after every render. Knowing these nuances helps in writing more efficient and bug-free React components.

Common Challenges with useEffect ⚠️

Using the useEffect hook in React can sometimes introduce challenges that developers need to be mindful of. Here are some general problems often encountered:

1. Infinite Loops: A common issue with useEffect is unintentionally creating infinite loops. This occurs when the effect updates a state or prop that it's dependent on, causing it to re-run endlessly.

2. Memory Leaks: Asynchronous operations within useEffect, like API calls or subscriptions, can lead to memory leaks if not properly cleaned up. This typically occurs when a component unmounts before an operation completes.

Conclusion ✅

In this blog, we’ve seen how the useEffect hook in React helps manage a component's behavior in a simple and effective way. It's great for tasks like fetching data, updating the component, and cleaning up. Just remember to use it carefully to avoid common issues. With a good grasp of useEffect, you can make your React projects smoother and more powerful.


下载开源日报APP:https://openingsource.org/2579/
加入我们:https://openingsource.org/about/join/
关注我们:https://openingsource.org/about/love/