React Hooks是什么?

首先

由于在公司内进行了React Hooks学习会,作为会议记录和产出,我将写一篇文章。

React Hooks 是什么?

React16.8引入了一项功能,使得函数组件能够使用状态管理和生命周期等功能。

React Hooks诞生的背景

在React中,有两种编写方式:类组件和函数组件。以下是一个使用类组件编写的代码示例,通过按钮来切换文本内容。

import React from "react";

class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      isLogin: true
    }
  }

  toggleLoginStatus = () => {
    this.setState({isLogin: !this.state.isLogin});
  }

  render() {
    return (
      <>
        {this.state.isLogin ? "ログイン中" : "ログアウト中"}
        <button onClick={this.toggleLoginStatus}>toggle</button>
      </>
    )
  }
}

export default Hoge

通过按下按钮来改变isLogin的状态,并进行文本的切换。
据说在一段时间前,使用类组件来编写代码是常见的做法。
原因是,类组件可以使用状态管理、生命周期等功能,而函数组件无法使用这些功能。

React Hooks的出现解决了这个问题。
有了React Hooks,函数组件可以使用状态管理和生命周期功能,并且可以编写具有高可读性的代码。
下面是一个使用函数组件编写的与之前示例相同的版本。

import { useState } from "react";

const Hoge = () => {
  const [isLogin, setIsLogin] = useState(true);

  const toggleLoginStatus = () => {
    setIsLogin(isLogin => !isLogin);
  }

  return (
    <>
      {isLogin ? "ログイン中" : "ログアウト中"}
      <button onClick={toggleLoginStatus}>toggle</button>
    </>
  );
}

export default Hoge

我想您可能会注意到,与类组件相比,可以更简洁地编写相同的处理过程。

介绍React Hooks

简单来说,我会介绍一下React Hooks。

使用状态

useState是用于保持状态的钩子,在React Hooks中是最常用的钩子。它可以用来保持各种不同的状态,例如数字、布尔值、映射等。以下是一个示例,当按钮被点击时计数会增加。

import { useState } from "react";

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

  const countUp = () => {
    setCount(count => count + 1);
  }

  return (
    <>
      <p>今のカウント:{count}</p>
      <button onClick={countUp}>
        count up!
      </button>
    </>
  );
}

export default Hoge

使用使用效果

useEffect是一個用於實現生命周期(在類組件中稱為componentDidMount和componentDidUpdate, componentWillUnmount)的鉤子,它可以在渲染時或狀態更新時運行useEffect內的副作用函數。

只想在首次渲染时执行处理。

在useEffect的第二个参数中传入一个空的依赖数组。

// レンダリングされた時にのみ実行(componentDidMount)
  useEffect(() => {
    console.log("component did mount");
  }, []);
当需要在特定的状态被更新时执行处理时

在useEffect的第二个参数中指定要监视的状态。

// countが更新された時に実行(componentDidUpdate)
  useEffect(() => {
    console.log("component did update");
    console.log(`current count is ${count}`);
  }, [count])
关于组件的删除(卸载时)

通过在useEffect中返回函数,可以在组件卸载时删除执行的副作用函数。通过这样做可以避免意外的错误。返回的函数被称为清理函数。

// コンポーネントが削除される際に実行(componentWillUnmount)
  useEffect(() => {
    return () => {
      console.log("component will unmount");
    };
  }, [])

这里是总结以上内容的一份文件。

import { useState, useEffect } from "react";

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

  const countUp = () => {
    setCount(count => count + 1);
  }

  useEffect(() => {
    console.log("component did mount");
  }, []);

  useEffect(() => {
    console.log("component did update");
    console.log(`current count is ${count}`);
  }, [count])

  useEffect(() => {
    return () => {
      console.log("component will unmount");
    };
  }, [])

  return (
    <>
      今のカウント:{count}
      <button onClick={countUp}>
        count up!
      </button>
    </>
  );
}

使用上下文

我想你可能会经常遇到需要从父组件向子组件传递值的情况,而有时也可能需要将值从父组件传递给孙子组件,具体情况而定。
在这种情况下,不需要通过父组件向子组件再向孙子组件传递props,可以使用useContext,这样就可以在任何层级上引用存储在Context中的值。
虽然这很方便,但需要注意的是,一旦更新了Context,使用该Context的所有组件都会重新渲染。
以下是在孙子组件中获取在父组件中定义的Context的代码。

import { useContext, createContext } from "react";

const user = {
  name: "Taro",
  age: 17
}

const UserContext = createContext(user);

const Hoge = () => {
  return (
    <UserContext.Provider value={user}>
      <User />
    </UserContext.Provider>
  );
}

const User = () => {
  return(
    <UserProfile />
  )
}

const UserProfile = () => {
  const userInfo = useContext(UserContext);
  return (
    <>
      <p>名前:{userInfo.name}</p>
      <p>年齢:{userInfo.age}</p>
    </>
  );
}

export default Hoge

使用useReducer

useReducer跟useState一样,是一个用于管理状态的Hook,在需要根据多个值或条件来更新状态时主要使用。
它接收一个拥有(state, action) => newState类型的reducer函数,并返回当前状态和dispatch方法。
通过在onClick等事件中执行带有action作为参数的dispatch方法,reducer将被调用,并根据action来更新状态。
由于useReducer只返回newState,所以如果状态是以对象形式定义的,必须以对象的形式返回。

import { useReducer } from "react";

const reducer = (state: any, action: any) => {
  switch (action) {
    case "increment":
      return { count: state.count + 1 }
    case "decrement":
      return { count: state.count - 1 }
    default:
      throw new Error();
  }
}

const Hoge = () => {
  const [countState, dispatch] = useReducer(reducer, { count: 0 });
  return (
    <>
      <p>今のカウント:{countState.count}</p>
      <button onClick={() => dispatch("decrement")}>decrement</button>
      <button onClick={() => dispatch("increment")}>increment</button>
    </>
  );
}

export default Hoge

使用useCallback

通过使用useCallback,可以将函数进行缓存,并以回调函数的形式返回,从而避免不必要的重新渲染。useCallback经常与React.memo一起使用。

React.memo 是什么?

React.memo是用于包装希望进行记忆化的组件的函数。记忆化是指如果props没有差异,则跳过重新渲染的过程,并记住上次渲染的组件结果。

然而,当作为prop接收回调函数时,即使使用了React.memo,组件仍会被重新渲染。这是因为即使函数内容相同,每次组件重新渲染时函数都会被重新创建,因此它们不会被视为与上一次相等。

因此,我们可以使用useCallback来对回调函数进行记忆化,以防止不必要的重新渲染。

下面是一个示例代码,演示了如何在使用React.memo进行了记忆化的组件中传递使用useCallback进行了记忆化的回调函数。

通过这种方式,当增加计数(更新状态)时,只会重新渲染被点击的组件。

import { React.memo, useCallback } from "react";

const Button = React.memo(({text, handleClick}) => {
  // 確認用
  console.log(text);
  return (
    <button onClick={handleClick}>{text}</button>
  );
});

const Hoge = () => {
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);

  const handleClick1 = useCallback(() => setCount1(count1 + 1), [count1]);
  const handleClick2 = useCallback(() => setCount2(count2 + 1), [count2]);

  return (
    <>
      <p>count1:{count1}</p>
      <p>count2:{count2}</p>
      <Button text={"count1:increment"} handleClick={handleClick1} />
      <Button text={"count2:increment"} handleClick={handleClick2} />
    </>
  );
}

export default Hoge

使用 useMemo

useMemo是一个用于保存函数结果的钩子,通过避免不必要的重新计算,在重新渲染时可以提高性能。
上述的useCallback是对函数本身进行记忆化,而useMemo是对函数的结果进行记忆化。
以下是一个使用useMemo来保存计算结果,并在依赖的状态更新时才重新计算的代码示例。

import { useMemo } form "react";

const Hoge = () => {
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);

  const twiceCount1 = useMemo(() => {
    return 2 * count1;
  }, [count1]);

  const handleClick1 = () => setCount1(count1 => count1 + 1);
  const handleClick2 = () => setCount2(count2 => count2 + 1);

  return (
    <>
      <p>count1:{count1}</p>
      <p>count2:{count2}</p>
      <button onClick={handleClick1}>count1:increment</button>
      <button onClick={handleClick2}>count2:increment</button>
      <p>count1:{twiceCount1}</p>
    </>
  );
}

export default Hoge

使用useRef

useRef和useState一样,可以在组件中保存值。
与useState不同的是,当更新值时不会触发重新渲染。
因此,它通常用于处理与渲染无关的值的用例。

下面是使用例,展示了获取上一个状态的代码。※请参考官方文档
在绘制之后,可以使用useEffect来保持prevCount中的上一个状态,并在下次重新绘制时显示prevCount。

import { useRef } from "react";

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

  const prevCountRef = useRef(0);
  useEffect(() => {
    prevCountRef.current = count;
  });
  const prevCount = prevCountRef.current;

  return (
    <>
      <p>今のカウント:{count}</p>
      <p>前回のカウント:{prevCount}</p>
      <button onClick={() => setCount(count+1)}></button>
    </>
  )
}

export default Hoge

总之

我简单地介绍了React Hooks的起源和官方Hooks。
我自己并没有很好地使用useCallback、useMemo和React.memo这些概念,通过这次记录我重新确认了这些基本概念。
我将努力在今后的开发和重构中编写性能优化的代码。

广告
将在 10 秒后关闭
bannerAds