React Hooks(一)

  • useState

  • 基本语法 

    const [state, setState] = useState(initialState);
  • 注意事项  

  • initialState只在初次渲染时保存为初始状态,后续渲染时将其忽略。如果传递函数的返回值作为initialState,那么这个函数应该不接受任何参数,并且要有一个返回值,此时,为了避免重复创建初始状态,传递的应该是函数本身,而不是函数调用的结果
    const [todos, setTodos] = useState(createInitialTodos);
    //如果传递的是函数本身,则React仅在初始化期间调用它
    const [todos, setTodos] = useState(createInitialTodos());
    //若传递的是函数调用的结果,尽管 createInitialTodos() 的结果仅用于初始渲染,
    //但在每次重新渲染时都会调用此函数,这可能造成资源的浪费
  •  若想传递一个函数作为state,则应该使用箭头函数的形式
    const [fn, setFn] = useState(someFunction);
    function handleClick() {
      setFn(someOtherFunction);
    }
    //如果传递的直接是一个函数,则React 认为 someFunction 是一个初始化函数,
    //而someOtherFunction 是一个更新函数,于是会调用函数并存储函数调用的结果
    
    const [fn, setFn] = useState(() => someFunction);
    
    function handleClick() {
      setFn(() => someOtherFunction);
    }
    //通过这种方式,React 将存储传递的函数作为state
  • setState的两种方式:直接传递新状态,或者传递一个根据先前状态来计算新状态的函数。调用set函数不会改变已经执行的代码中当前的state,它只影响下一次渲染中useState返回的内容。传递更新函数,是获取的待定状态,并从中计算下一个状态。如果你提供的新值与当前state相同(由 Object.is 比较确定),React 将 跳过重新渲染该组件及其子组件
    const [name, setName] = useState('Edward');
    const [age, setAge] = useState(18);
    
    function handleClick() {
      setName('Taylor');
      console.log(name) //依然是Edward
      setAge(a => a + 1);
    }
     传递更新函数和直接传递下一个状态之间的区别:
    function handleClick() {
      setAge(age + 1); // setAge(42 + 1)
      setAge(age + 1); // setAge(42 + 1)
      setAge(age + 1); // setAge(42 + 1)
    }
    //这样的方式age将只会变为43而不是45
    
    function handleClick() {
      setAge(a => a + 1); // setAge(42 => 43),42作为待定状态
      setAge(a => a + 1); // setAge(43 => 44),43作为待定状态
      setAge(a => a + 1); // setAge(44 => 45),44作为待定状态
    }
    //这里的a => a + 1 是更新函数。它获取待定状态,并从中计算下一个状态,age最后会变成45
  • 出现错误:“Too many re-renders”(可能是在渲染过程中调用了事件处理函数) 
    // 🚩 错误:在渲染过程中调用事件处理函数
    return <button onClick={handleClick()}>Click me</button>
    
    // ✅ 正确:将事件处理函数传递下去
    return <button onClick={handleClick}>Click me</button>
    
    // ✅ 正确:传递一个内联函数
    return <button onClick={(e) => handleClick(e)}>Click me</button>
  • useEffect 

  •  基本语法 

    useEffect(() => {
        //setup
        const connection = createConnection(serverUrl, roomId);
        connection.connect();
        //cleanup
        return () => {
          connection.disconnect();
        };
      }, [serverUrl, roomId]);
  • 代码执行顺序  

1. 将组件挂载到页面时,将运行 setup 代码。

2. 重新渲染 依赖项 变更的组件后

  • 首先,使用旧的 props 和 state 运行 cleanup 代码。
  • 然后,使用新的 props 和 state 运行 setup 代码。

3. 当组件从页面卸载后,cleanup 代码 将运行最后一次。 

  •  删除不必要的对象依赖项  

    const options = { // 🚩 这个对象在每次渲染时都是从头创建的
        serverUrl: serverUrl,
        roomId: roomId
      };
    
      useEffect(() => {
        const connection = createConnection(options); // 它在 Effect 内部使用
        connection.connect();
        return () => connection.disconnect();
      }, [options]); // 🚩 因此,这些依赖在重新渲染时总是不同的
    
    
    
    //应该改为:
    useEffect(() => {
        const options = {
          serverUrl: serverUrl,
          roomId: roomId
        };
        const connection = createConnection(options);
        connection.connect();
        return () => connection.disconnect();
      }, [roomId]);
  •  删除不必要的函数依赖项  

    function createOptions() { // 🚩 此函数在每次重新渲染都从头开始创建
        return {
          serverUrl: serverUrl,
          roomId: roomId
        };
      }
    
      useEffect(() => {
        const options = createOptions(); // 它在 Effect 中被使用
        const connection = createConnection();
        connection.connect();
        return () => connection.disconnect();
      }, [createOptions]); // 🚩 因此,此依赖项在每次重新渲染都是不同的
    
    
    //应改为:
    useEffect(() => {
        function createOptions() {
          return {
            serverUrl: serverUrl,
            roomId: roomId
          };
        }
    
        const options = createOptions();
        const connection = createConnection(options);
        connection.connect();
        return () => connection.disconnect();
      }, [roomId]);
  • useLayoutEffect 

  • 和useEffect的区别 

        useLayoutEffec是useEffect的一个版本,在浏览器重新绘制屏幕之前触发,会阻塞浏览器的渲染,而useEffect不会阻塞浏览器的渲染

  • useContext

  • 基本语法 

    const value = useContext(SomeContext);
  • 返回值  

        为传递给树中调用组件上方最近的 SomeContext.Providervalue。如果没有这样的 provider,那么返回值将会是为创建该 context 传递给 createContext 的defaultValue。返回的值始终是最新的,如果 context 发生变化,React 会自动重新渲染读取 context 的组件。 

  • 用法一:向组件树深层传递数据

    import { createContext, useContext } from 'react';
    
    const ThemeContext = createContext(null);
    
    export default function MyApp() {
      const [theme, setTheme] = useState('dark');
      return (
        <ThemeContext.Provider value={theme}>
          <Form />
        </ThemeContext.Provider>
      )
    }
    
    function Form() {
      return (
        <Panel title="Welcome">
          <Button>Sign up</Button>
          <Button>Log in</Button>
        </Panel>
      );
    }
    
    function Panel({ title, children }) {
      const theme = useContext(ThemeContext);
      const className = 'panel-' + theme;
      return (
        <section className={className}>
          <h1>{title}</h1>
          {children}
        </section>
      )
    }
    
    function Button({ children }) {
      const theme = useContext(ThemeContext);
      const className = 'button-' + theme;
      return (
        <button className={className}>
          {children}
        </button>
      );
    }
    
  • 用法二:在传递对象和函数时优化重新渲染

    function MyApp() {
      const [currentUser, setCurrentUser] = useState(null);
    
      function login(response) {
        storeCredentials(response.credentials);
        setCurrentUser(response.user);
      }
    
      return (
        <AuthContext.Provider value={{ currentUser, login }}>
          <Page />
        </AuthContext.Provider>
      );
    }
    //注:通过 context 可以传递任何值,包括对象和函数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值