ABOUT ME

Today
Yesterday
Total
  • The Effect Hook
    React 2021. 12. 6. 18:17

    이 글은 React의 Effect Hook에 관한 개념을 정리하기 위해 작성하였습니다.




    < Why Use useEffect? >

    A function is said to have a Side Effect if its implementation inside a function affects outside the function.

    In earlier versions of React, we could only have executed this type of code in the lifecycle methods of class components


     

    < We use the Effect Hook to run some JavaScript's Side Effect code after each render, such as : >
    1. fetching data from a backend service
    2. subscribing to a stream of data
    3. managing timers and intervals
    4. reading from and making changes to the DOM


    < Why after each render? >
    Most interesting components will re-render multiple times throughout their lifetime and these key moments present the perfect opportunity to execute these “side effects”.
    Because in these moments the rendering is not affected.

    There are three key moments when the Effect Hook can be utilized(When the Effect Hook runs) :
    1. When the component is first added, or mounted, to the DOM and renders
    2. When the state or props change, causing the component to re-render
    3. When the component is removed, or unmounted, from the DOM.

     

    Example: Class Component vs Function Component

    import React, {Component} from 'react';
    
    export default class PageTitle extends Component {
      constructor(props) {
        super(props);
        this.state = {
          name: ''
        };
      }
    
      componentDidMount() {
        document.title = this.state.name;
      }
      
      componentDidUpdate() {
        document.title == `Hi, ${this.state.name}`;
      }
    
      render() {
        return (
          <div>
            <p>Use the input field below to rename this page!</p>
            <input 
              onChange={({target}) => this.setState({ name: target.value })} 
              value={this.state.name} 
              type='text' />
          </div>
        );
      }
    }

    : Class Component

    import React, { useState, useEffect } from 'react';
     
    export default function PageTitle() {
      const [name, setName] = useState('');
     
     useEffect(() => {
        document.title = `Hi, ${name}`;
      }, [name]);
     
      return (
        <div>
          <p>Use {name} input field below to rename this page!</p>
          <input 
            onChange={({target}) => setName(target.value)} 
            value={name} 
            type='text' />
        </div>
      );
    }

    : Function Component


    < Function Component Effects >

    To use the Effect Hook, use useEffect( ) method.

     

    Example)

    import React, { useState, useEffect } from 'react';
     
    function PageTitle() {
      const [name, setName] = useState('');
     
      useEffect(() => {
        document.title = `Hi, ${name}`;
      });
     
      return (
        <div>
          <p>Use the input field below to rename this page!</p>
          <input onChange={({target}) => setName(target.value)} value={name} type='text' />
        </div>
      );
    }

     

    • The Effect Hook is a named export from the React library, so we import it like this :
      import { useEffect } from 'react';
       
    • The first argument passed to the useEffect( ) function is the callback function that we want React to call after each time this component renders. We refer to this callback function as our effect.
      // In our example, the effect is :
      () => { document.title = name; }

     


    < Clean Up Effects >

    Some effects require cleanup.
    For example, when we want to add event listeners to some element in the DOM, it is important to remove those event listeners when we are done with them to avoid memory leaks!

     

    Example)

    useEffect(()=>{
      document.addEventListener('keydown', handleKeyPress);
      return () => {
        document.removeEventListener('keydown', handleKeyPress);
      };
    })
    • If our effect returns a function, then the useEffect( ) Hook always treats that as a cleanup function.
      (this cleanup function is optional)

    • Effect Hook will call this cleanup function before that effect runs as well as when the component is being unmounted
    • If our effect didn't return a cleaup function, then a new event listener would be added to the DOM’s document object every time that our component re-renders.

     

     


    < Control When Effects Are Called >

    To run an effect only when the component mounts (renders the first time), but not when the component re-renders.

     

    < The Dependency Array >
    To only call our effect after the first render, pass an empty array to useEffect( ) as the second argument. This second argument is called the dependency array.

    The dependency array is used to tell the useEffect( ) method when to call our effect and when to skip it.
    Our effect is always called after the first render but only called again if something in our dependency array has changed values between renders.

     

    Example)

    useEffect(() => {
      alert("component rendered for the first time");
      return () => {
        alert("component is being removed from the DOM");
      };
    }, []);
    
    // Only re-run the effect if the value stored by count changes
    useEffect(() => {
      document.title = `You clicked ${count} times`;
    }, [count]);

    : If a cleanup function is returned by our effect, calling that when the component unmounts.

     

    Example in Practice)

    import React, { useState, useEffect } from 'react';
    
    export default function Timer() {
      const [time, setTime] = useState(0);
      const [name, setName] = useState('');
    
      useEffect(() => {
        const intervalId = setInterval(() => {
          setTime((prev) => prev + 1);
        }, 1000);
        return () => {clearInterval(intervalId)};
      }, []);
    
      const handleChange = ({ target }) => setName(target.value);
    
      return (
        <>
          <h1>Time: {time}</h1>
          <input value={name} onChange={handleChange} type='text' />
        </>
      );
    }

     

     


    < Rules of Hooks >

    There are two main rules to keep in mind when using Hooks:
    1. only call Hooks at the top level
    2. only call Hooks from React functions

     

    < First : Only call hooks at the top level >
    When React builds the Virtual DOM, the library calls the functions that define our components over and over again as the user interacts with the user interface. React keeps track of the data and functions that we are managing based on their order in the function component’s definition.
    For this reason, we always call our Hooks at the top level; we never call hooks inside of loops, conditions, or nested functions.

    Instead of confusing React with code like this:
    if (userName !== '') {
      useEffect(() => {
        localStorage.setItem('savedUserName', userName);
      });
    }
    use this code :
    useEffect(() => {
      if (userName !== '') {
        localStorage.setItem('savedUserName', userName);
      }
    });​

     

    < Second : Only call Hooks from React functions >
    We cannot use Hooks in class components and we cannot use Hooks in regular JavaScript functions.

     

     


    < Separate Hooks for Separate Effects >

    When multiple values are closely related and change at the same time, it can make sense to group these values in a collection like an object or array. Packaging data together can also add complexity to the code responsible for managing that data.
    Therefore, it is a good idea to separate concerns by managing different data with different Hooks.

     

    Example) Instead of confusing code like this :

    // Handle both position and menuItems with one useEffect hook.
    const [data, setData] = useState({ position: { x: 0, y: 0 } });
    useEffect(() => {
      get('/menu').then((response) => {
        setData((prev) => ({ ...prev, menuItems: response.data }));
      });
      const handleMove = (event) =>
        setData((prev) => ({
          ...prev,
          position: { x: event.clientX, y: event.clientY }
        }));
      window.addEventListener('mousemove', handleMove);
      return () => window.removeEventListener('mousemove', handleMove);
    }, []);

    Use this code :

    // Handle menuItems with one useEffect hook.
    const [menuItems, setMenuItems] = useState(null);
    useEffect(() => {
      get('/menu').then((response) => setMenuItems(response.data));
    }, []);
     
    // Handle position with a separate useEffect hook.
    const [position, setPosition] = useState({ x: 0, y: 0 });
    useEffect(() => {
      const handleMove = (event) =>
        setPosition({ x: event.clientX, y: event.clientY });
      window.addEventListener('mousemove', handleMove);
      return () => window.removeEventListener('mousemove', handleMove);
    }, []);

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    'React' 카테고리의 다른 글

    propTypes  (0) 2021.12.07
    Style  (0) 2021.12.06
    The State Hook  (0) 2021.12.06
    Function Components  (0) 2021.12.05
    Component Lifecycle  (0) 2021.12.02

    댓글

Designed by Tistory.