学习React Hook

105 阅读3分钟

useState

官方文档 useState

更新状态中的对象和数组 

对象

const [form, setForm] = useState({
    firstName: 'Barbara',
    lastName: 'Hepworth',
    email: 'bhepworth@sculpture.com',
  });

 <input
          value={form.firstName}
          onChange={e => {
            setForm({
              ...form,
              firstName: e.target.value
            });
          }}
        />

嵌套对象

 const [person, setPerson] = useState({
    name: 'Niki de Saint Phalle',
    artwork: {
      title: 'Blue Nana',
      city: 'Hamburg',
      image: 'https://i.imgur.com/Sd1AgUOm.jpg',
    }
  });

 function handleNameChange(e) {
    setPerson({
      ...person,
      name: e.target.value
    });
  }
  
  function handleTitleChange(e) {
    setPerson({
      ...person,
      artwork: {
        ...person.artwork,
        title: e.target.value
      }
    });
  }

数组

import { useState } from 'react';
import AddTodo from './AddTodo.js';
import TaskList from './TaskList.js';

let nextId = 3;
const initialTodos = [
  { id: 0, title: 'Buy milk', done: true },
  { id: 1, title: 'Eat tacos', done: false },
  { id: 2, title: 'Brew tea', done: false },
];

export default function TaskApp() {
  const [todos, setTodos] = useState(initialTodos);

  function handleAddTodo(title) {
    setTodos([
      ...todos,
      {
        id: nextId++,
        title: title,
        done: false
      }
    ]);
  }

  function handleChangeTodo(nextTodo) {
    setTodos(todos.map(t => {
      if (t.id === nextTodo.id) {
        return nextTodo;
      } else {
        return t;
      }
    }));
  }

  function handleDeleteTodo(todoId) {
    setTodos(
      todos.filter(t => t.id !== todoId)
    );
  }

  return (
    <>
      <AddTodo
        onAddTodo={handleAddTodo}
      />
      <TaskList
        todos={todos}
        onChangeTodo={handleChangeTodo}
        onDeleteTodo={handleDeleteTodo}
      />
    </>
  );
}

问题

更新了状态,但日志仍显示旧

function handleClick() {  
console.log(count); // 0  
setCount(count + 1); // 请求使用 1 重新渲染  

console.log(count); // 仍然是 0!  

setTimeout(() => {  

console.log(count); // 还是 0!  

}, 5000);  

}

如果你需要使用下一个状态,你可以在将其传递给 set 函数之前将其保存在一个变量中:

const nextCount = count + 1;  
setCount(nextCount);  
console.log(count); // 0 
console.log(nextCount); // 1

将 state 设置为一个函数,但它却被调用了

不能像这样把函数放入状态:

const [fn, setFn] = useState(someFunction);  

function handleClick() {  
 setFn(someOtherFunction);  
}

因为你传递了一个函数,React 认为 someFunction 是一个 初始化函数,而 someOtherFunction 是一个 更新函数,于是它尝试调用它们并存储结果。要实际 存储 一个函数,你必须在两种情况下在它们之前加上 () =>。然后 React 将存储你传递的函数。

const [fn, setFn] = useState(() => someFunction);  

 function handleClick() {  
  setFn(() => someOtherFunction);  
}

useEffect(setup, dependencies?)

用法

  • setup:处理 Effect 的函数。setup 函数选择性返回一个 清理(cleanup) 函数。当组件被添加到 DOM 的时候,React 将运行 setup 函数。在每次依赖项变更重新渲染后,React 将首先使用旧值运行 cleanup 函数(如果你提供了该函数),然后使用新值运行 setup 函数。在组件从 DOM 中移除后,React 将最后一次运行 cleanup 函数。
import { useEffect, useRef } from 'react';

export default function ModalDialog({ isOpen, children }) {
  const ref = useRef();

  useEffect(() => {
    if (!isOpen) {
      return;
    }
    const dialog = ref.current;
    dialog.showModal();
    return () => {
      dialog.close();
    };
  }, [isOpen]);

  return <dialog ref={ref}>{children}</dialog>;
}

指定响应式依赖项

注意,你无法“选择” Effect 的依赖项。Effect 代码中使用的每个 响应式值 都必须声明为依赖项。你的 Effect 依赖列表是由周围代码决定的:

function ChatRoom({ roomId }) { // 这是一个响应式值  

const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // 这也是一个响应式值  

useEffect(() => {  

const connection = createConnection(serverUrl, roomId); // 这个 Effect 读取这些响应式值  

connection.connect();  

return () => connection.disconnect();  

}, [serverUrl, roomId]); // ✅ 因此你必须指定它们作为 Effect 的依赖项  


// ...  


}

不需要依赖项的情况

现在 serverUrl 不再是一个响应式值(并且在重新渲染时也不会更改),它就不需要成为一个依赖项。如果 Effect 的代码不使用任何响应式值,则其依赖项列表应为空([]

const serverUrl = 'https://localhost:1234'; // 不再是响应式值  


const roomId = 'music'; // 不再是响应式值  

function ChatRoom() {  

useEffect(() => {  

 const connection = createConnection(serverUrl, roomId);  

 connection.connect();  

return () => connection.disconnect();  


}, []); // ✅ 所有声明的依赖项  


// ...  


}

传递响应式依赖

传递依赖项数组

如果指定了依赖项,则 Effect 在 初始渲染后以及依赖项变更的重新渲染后 运行。

useEffect(() => {  

}, [a, b]); // 如果 a 或 b 不同则会再次运行

传递空依赖项数组 

如果你的 Effect 确实没有使用任何响应式值,则它仅在 初始渲染后 运行。

useEffect(() => {  


}, []); // 不会再次运行(开发环境下除外)

不传递依赖项数组

如果完全不传递依赖数组,则 Effect 会在组件的 每次单独渲染(和重新渲染)之后 运行。

useEffect(() => {  
// ...  


}); // 总是再次运行