React Hooks

为什么使用 React Hook

6 Reasons to Use React Hooks Instead of Classes

useState hook

  • useState hook用来render state variable
  • 标准写法: const [stateVariable, setStateVariable function] = useState();
import React, { useState } from "react";

const StateTutorial = () => {
  //inputValue是state variable
  //setInputValue是function
  //只能通过setInputValue对state variable进行修改
  const [inputValue, setInputValue] = useState("Pedro"); 

  let onChange = (event) => {
    //因为setInputValue是异步的, 所以需要先将event.target赋值出来
    const newValue = event.target.value;
    setInputValue(newValue);
  };

  return (
    <div>
      <input placeholder="enter something..." onChange={onChange} />
      {inputValue}
    </div>
  );
};

export default StateTutorial;

实现效果: 右边同时更新左边输入的字符串
在这里插入图片描述

实际开发例子:

  • state variable selectedChildren 是一个数组
  • 通过onChange调用setSeelctedChildren, 对selectedChildren进行修改
  • selectedChildren通过value props传给组件, 渲染到屏幕上

在这里插入图片描述
在这里插入图片描述

  • setStateVariable function不是Web API和server call 为什么还是异步的?
  • This is because setState alters the state and causes rerendering. This can be an expensive operation and making it synchronous might leave the browser unresponsive. Thus the setState calls are asynchronous as well as batched for better UI experience and performance.

useReducer hook

  • 当同时需要update多个state时可以使用useReducer
const ReducerTutorial = () => {
	const [count, setCount] = useState(0);
	const [showText, setShowText] = useState(true);
	
	return (
    <div>
      <h1>{state.count}</h1>
      <button
        onClick={() => {
        	setCount(count + 1);
        	setShowText(!showText);
        }}
      >
        Click Here
      </button>

      {state.showText && <p>This is a text</p>}
    </div>
  );
}

上面的代码可以使用useReducer完成, 如下

import React, { useReducer } from "react";

const reducer = (state, action) => {
  switch (action.type) {
    case "INCREMENT":
      return { count: state.count + 1, showText: state.showText };
    case "toggleShowText":
      return { count: state.count, showText: !state.showText };
    default:
      return state;
  }
};

const ReducerTutorial = () => {
  const [state, dispatch] = useReducer(reducer, { count: 0, showText: true });

  return (
    <div>
      <h1>{state.count}</h1>
      <button
        onClick={() => {
          dispatch({ type: "INCREMENT" });
          dispatch({ type: "toggleShowText" });
        }}
      >
        Click Here
      </button>

      {state.showText && <p>This is a text</p>}
    </div>
  );
};

export default ReducerTutorial;

useEffect hook

  • 当每次页面render时, useEffect都会被执行
  • 除去刷新页面外, state variable被rerender时也会执行 useEffect

标准写法:

useEffect(() => { 
	do something ); 
 }, []);    
  • 后面的[]表示哪些state variable被更新时会执行useEffect
  • [ ] 为空表示任何state varibale更新时都不会执行useEffect. 只有按刷新页面时才会执行useEffect
  • 如果没有 [ ] 则表示任何state varibale更新时都会执行useEffect
import React, { useEffect, useState } from "react";
import axios from "axios";

function EffectTutorial() {
  const [data, setData] = useState("");
  const [count, setCount] = useState(0);

//刷新页面时, 会调用API
  useEffect(() => {
    axios
      .get("https://jsonplaceholder.typicode.com/comments")
      .then((response) => {
        setData(response.data[0].email);
        console.log("API WAS CALLED");
      });
  }, []);

  return (
    <div>
      Hello World
      <h1>{data}</h1>
      <h1>{count}</h1>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        Click![在这里插入图片描述](https://im![在这里插入图片描述](https://img-blog.csdnimg.cn/cc6c700276d04ee084fac886150d282c.png)
g-blog.csdnimg.cn/30669808ec2e4f68b78041512bf23964.png)

      </button>
    </div>
  );
}

export default EffectTutorial;

useRef hook

  • persist values between renderers beside use state. — 也就是在不渲染画面的情况下, 可以在component中储存变量
    在这里插入图片描述
    在这里插入图片描述

useLayoutEffect hook

  • useEffect会在渲染的内容到DOM上后执行,不会阻塞DOM的渲染
  • 也就是先渲染页面 (会显示未更新的state variable), 然后再更新state variable. (因为渲染和更新的非常快, 所以察觉不出来)
  • useLayoutEffect会在渲染的内容到DOM上之前进行,会阻塞DOM的渲染
import { useLayoutEffect, useEffect, useRef } from "react";

function LayoutEffectTutorial() {
  const inputRef = useRef(null);
  
  
  useLayoutEffect(() => {
  //先执行这里
    console.log(inputRef.current.value);
  }, []);
  
  useEffect(() => {
  //再执行这里
    inputRef.current.value = "HELLO";
  }, []);

  return (
    <div className="App">
      <input ref={inputRef} value="PEDRO" style={{ width: 400, height: 60 }} />
    </div>
  );
}

export default LayoutEffectTutorial;

useImperativeHandle hook

  • 可以在上级组件中控制子组件的state
import React, { useRef } from "react";
import Button from "./Button";

function ImperativeHandle() {
  const buttonRef = useRef(null);
  return (
    <div>
      <button
        onClick={() => {
          buttonRef.current.alterToggle();
        }}
      >
        Button From Parent
      </button>
      <Button ref={buttonRef} />
    </div>
  );
}

export default ImperativeHandle;
import React, { forwardRef, useImperativeHandle, useState } from "react";

const Button = forwardRef((props, ref) => {
  const [toggle, setToggle] = useState(false);

  useImperativeHandle(ref, () => ({
    alterToggle() {
      setToggle(!toggle);
    },
  }));
  return (
    <>
      <button>Button From Child</button>
      {toggle && <span>Toggle</span>}
    </>
  );
});

export default Button;

useMemo

  • 用于性能优化
import axios from "axios";
import { useEffect, useState, useMemo } from "react";

export default function MemoTutorial() {
  const [data, setData] = useState(null);
  const [toggle, setToggle] = useState(false);

  useEffect(() => {
    axios
      .get("https://jsonplaceholder.typicode.com/comments")
      .then((response) => {
        setData(response.data);
      });
  }, []);

  const findLongestName = (comments) => {
    if (!comments) return null;

    let longestName = "";
    for (let i = 0; i < comments.length; i++) {
      let currentName = comments[i].name;
      if (currentName.length > longestName.length) {
        longestName = currentName;
      }
    }

    console.log("THIS WAS COMPUTED");

    return longestName;
  };
  
  return (
    <div className="App">
      //这里每当页面渲染, 都会执行这个函数, 如果数据量很大, 会降低性能
      <div> {findLongestName(data)} </div>

      <button
        onClick={() => {
          setToggle(!toggle);
        }}
      >
        {" "}
        Toggle
      </button>
      {toggle && <h1> toggle </h1>}
    </div>
  );
}

使用useMemo改进

import axios from "axios";
import { useEffect, useState, useMemo } from "react";

export default function MemoTutorial() {
  const [data, setData] = useState(null);
  const [toggle, setToggle] = useState(false);

  useEffect(() => {
    axios
      .get("https://jsonplaceholder.typicode.com/comments")
      .then((response) => {
        setData(response.data);
      });
  }, []);

  const findLongestName = (comments) => {
    if (!comments) return null;

    let longestName = "";
    for (let i = 0; i < comments.length; i++) {
      let currentName = comments[i].name;
      if (currentName.length > longestName.length) {
        longestName = currentName;
      }
    }

    console.log("THIS WAS COMPUTED");

    return longestName;
  };

  const getLongestName = useMemo(() => findLongestName(data), [toggle]); //只有当data改变时才执行这个函数

  return (
    <div className="App">
      <div> {getLongestName} </div>

      <button
        onClick={() => {
          setToggle(!toggle);
        }}
      >
        {" "}
        Toggle
      </button>
      {toggle && <h1> toggle </h1>}
    </div>
  );
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值