react 常用hooks

197 阅读4分钟

useState

使用状态响应输入

import { useState } from 'react';

export default function Form() {
  const [answer, setAnswer] = useState('');
  const [error, setError] = useState(null);
  const [status, setStatus] = useState('typing');

  if (status === 'success') {
    return <h1>答对了!</h1>
  }

  async function handleSubmit(e) {
    e.preventDefault();
    setStatus('submitting');
    try {
      await submitForm(answer);
      setStatus('success');
    } catch (err) {
      setStatus('typing');
      setError(err);
    }
  }

  function handleTextareaChange(e) {
    setAnswer(e.target.value);
  }

  return (
    <>
      <h2>城市测验</h2>
      <p>
        哪个城市有把空气变成饮用水的广告牌?
      </p>
      <form onSubmit={handleSubmit}>
        <textarea
          value={answer}
          onChange={handleTextareaChange}
          disabled={status === 'submitting'}
        />
        <br />
        <button disabled={
          answer.length === 0 ||
          status === 'submitting'
        }>
          提交
        </button>
        {error !== null &&
          <p className="Error">
            {error.message}
          </p>
        }
      </form>
    </>
  );
}
function submitForm(answer) {
  // 模拟接口请求
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      let shouldError = answer.toLowerCase() !== 'lima'
      if (shouldError) {
        reject(new Error('猜的不错,但答案不对。再试试看吧!'));
      } else {
        resolve();
      }
    }, 1500);
  });
}

有时候,你希望两个组件的状态始终同步更改。要实现这一点,可以将相关 state 从这两个组件上移除,并把 state 放到它们的公共父级,再通过 props 将 state 传递给这两个组件。这被称为“状态提升”,这是编写 React 代码时常做的事。

useReducer

对于拥有许多状态更新逻辑的组件来说,过于分散的事件处理程序可能会令人不知所措。对于这种情况,你可以将组件的所有状态更新逻辑整合到一个外部函数中,这个函数叫作 reducer

**编写一个 reducer 函数 **

function tasksReducer(tasks, action) {
  switch (action.type) {
    case 'added': {
      return [
        ...tasks,
        {
          id: action.id,
          text: action.text,
          done: false,
        },
      ];
    }
    case 'changed': {
      return tasks.map((t) => {
        if (t.id === action.task.id) {
          return action.task;
        } else {
          return t;
        }
      });
    }
    case 'deleted': {
      return tasks.filter((t) => t.id !== action.id);
    }
    default: {
      throw Error('未知 action: ' + action.type);
    }
  }
}

**在组件中使用 reducer **

从 React 中导入 useReducer Hook

替换掉之前的 useState,使用 useReducer

const [tasks, setTasks] = useState(initialTasks);

import { useReducer } from 'react';
import AddTask from './AddTask.js';
import TaskList from './TaskList.js';

export default function TaskApp() {
  const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);

  function handleAddTask(text) {
    dispatch({
      type: 'added',
      id: nextId++,
      text: text,
    });
  }

  function handleChangeTask(task) {
    dispatch({
      type: 'changed',
      task: task,
    });
  }

  function handleDeleteTask(taskId) {
    dispatch({
      type: 'deleted',
      id: taskId,
    });

useContext

使用 Context 深层传递参数

通常来说,你会通过 props 将信息从父组件传递到子组件。但是,如果你必须通过许多中间组件向下传递 props,或是在你应用中的许多组件需要相同的信息,传递 props 会变的十分冗长和不便。Context 允许父组件向其下层无论多深的任何组件提供信息,而无需通过 props 显式传递

1、创建context

LevelContext.js

import {createContext} from 'react'
export const LevelContext = createContext(0);

2、使用context

Heading.js

import {useContext} from 'react'
import {LevelContext} from './LevelContext.js';

export default function Heading({children}) {
    const level = useContext(LevelContext);
    switch(level) {
            case 0throw Error('Heading 必须在Section内部')
            case 1:
            	return <h1>{children}</h1>;
            case 2:
              return <h2>{children}</h2>;
            case 3:
              return <h3>{children}</h3>;
            case 4:
              return <h4>{children}</h4>;
            case 5:
              return <h5>{children}</h5>;
            case 6:
              return <h6>{children}</h6>;
            default:
              throw Error('未知的 level:' + level);
    }
}

Section.js

import {useContext} from 'react'
import {LevelContect} from './LevelContext.js'

export default function Section({children}) {
    const level = useContext(LevelContext);
    
    return (
    	<section className="section">
            <LevelContext.Provider value={level}>
            	{children}
            </LevelContext.Provider>
        </section>
    )
}

App.js

import Heading from './Heading.js';
import Section from './Section.js';

export default function Page() {
  return (
    <Section>
      <Heading>主标题</Heading>
      <Section>
        <Heading>副标题</Heading>
        <Heading>副标题</Heading>
        <Heading>副标题</Heading>
        <Section>
          <Heading>子标题</Heading>
          <Heading>子标题</Heading>
          <Heading>子标题</Heading>
          <Section>
            <Heading>子子标题</Heading>
            <Heading>子子标题</Heading>
            <Heading>子子标题</Heading>
          </Section>
        </Section>
      </Section>
    </Section>
  );
}

useEffect

可以让你在函数组件中执行副作用操作

useEffect(setup, dependencies?)

类似React Component的生命周期。看做componentDidMount, componentDidUpdatecomponentWillUnmount这三个函数的组合

第二个参数dependencies:

1、没有传值时,组件的初始化和更新都会执行该方法;

2、一个空数组时,只在初始化调用一次之后不再执行,相当于componentDidMount;

3、传入第二个参数,只有一个值,该值有变化就执行,有多个值的数组,会比较每一个值,有一个不相等就执行。 数组里的[]元素变化,useEffect才会被再次调用,组件更新渲染,数组元素没变化,它并不会重新触发。

import { useEffect } from 'react';
import { createConnection } from './chat.js';

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => {
      connection.disconnect();
    };
  }, [serverUrl, roomId]);
  // ...
}

useRef

useRef 是一个 React Hook,它能让你引用一个不需要渲染的值。

const ref = useRef(initialValue)

参数

  • initialValue:ref 对象的 current 属性的初始值。可以是任意类型的值。这个参数会首次渲染后被忽略。

返回值

useRef 返回一个只有一个属性的对象:

  • current:最初,它被设置为你传递的 initialValue。之后你可以把它设置为其他值。如果你把 ref 对象作为一个 JSX 节点的 ref 属性传递给 React,React 将为它设置 current 属性。
import { useRef } from 'react';

function MyComponent() {
  const intervalRef = useRef(0);
  const inputRef = useRef(null);
  // ...

useRef 返回一个具有单个 current 属性 的 ref 对象,并初始化为你提供的 initial value

在后续的渲染中,useRef 将返回相同的对象。你可以改变它的 current 属性来存储信息,并在之后读取它。这会让你联想起 state,但是有一个重要的区别。

改变 ref 不会触发重新渲染。 这意味着 ref 是存储一些不影响组件视图输出的信息的完美选择。例如,如果你需要存储一个 intervalID 并在以后检索它,你可以把它放在一个 ref 中。如果要更新 ref 里面的值,你需要手动改变它的 current 属性:

function handleStartClick() {
  const intervalId = setInterval(() => {
    // ...
  }, 1000);
  intervalRef.current = intervalId;
}