useState、useEffect、useContext、useReducer、useCallback、useMemo、useLayoutEffect、seRef、路由相关的hooks等的使用

一、useState (使用状态)

1、定义状态:
const [状态名,更新状态的函数] = React.useState(初始值|函数);

如:声明一个新的叫做 “count” 的 state 变量,初始值为0 。

const [count, setCount] = React.useState(0);   //useState函数返回的是数组

相当于类组件中的
this.state={
    count :0
}

const [person, setPerson] = React.useState({name: '张三疯', age: 18,sex:"女"})
const [person, setPerson] = React.useState(() => ({name: '张三疯', age: 18,sex:"女"}))


2、读取值:
{count}
{person.name}   {person.age}


3、修改值:  
  setCount(5);

  //对于引用类型,不能局部更新(即:不能只改某个属性),所以,需要使用扩展运算符先拷贝以前所有的属性
  setPerson({
     ...person, //拷贝之前的所有属性
     age:person.age+1,
     name: '张四疯' //这里的name覆盖之前的name
 })

注意:
首先,需要知道,函数式组件重新渲染时,会执行函数里的所有代码,
那么,当函数式组件重新渲染时,会不会再次把状态的值恢复成初始值呢?答案是:不会。后续组件重新渲染时,会使用最后一次更新的状态值
【官网解释: React 会确保 setState 函数的标识是稳定的,并且不会在组件重新渲染时发生变化 】

二、useEffect 处理副作用 (生命周期)

注意
可以使得你在函数组件中执行一些带有副作用的方法,天哪,“副作用”(大脑中无数个????)。
每当 React组件更新之后,就会触发 useEffect,在第一次的render 和每次 update 后的render触发,不用再去考虑“初次挂载”还是“更新”。React 保证了每次运行 effect 的同时,DOM 都已经更新完毕。
你可以把 useEffect Hook 看做 componentDidMountcomponentDidUpdatecomponentWillUnmount 这三个函数的组合。
我们在函数式组件里,没有 componentDidMountcomponentDidUpdatecomponentWillUnmount,用useEffect。即:当数据发生变化后,渲染到组件上,组件渲染完毕后,就会调用useEffect。

useEffect(回调函数,[依赖]); //在render之后触发useEffect,进一步调用回调函数

1、useEffect的无条件执行(只有一个参数)

import React,{useState,useEffect} from 'react';

function App() {
  const [count,setCount] = useState(0);
  
  //useEffect:相当于 componentDidMount,componentDidUpdate
  useEffect(()=>{
      console.log("userEffect");
      document.title = count;
  });

  return (
    <div className="App">
      <p>{count}</p>
      <input type="button" value="测试" onClick={()=>{setCount(count+1)}} />
    </div>
  );
}

2、useEffect的条件执行(useEffect的第二个参数)

当useEffect只有一个参数时,会无条件执行,但是,当发送请求时(页面的初始数据来自后端),一旦把请求放在useEffect里,就会无休止的执行,因为,当请求的数据回来后,引起组件的更新,组件更新后,再次触发useEffect,再次发送请求,再次组件更新,陷入到了无限的死循环。那么,可以使用useEffect的第二个参数。
第二个参数表示,useEffect是否再次触发,依赖于某个值。当为空数组时,表示不会二次触发(因为,没有任何依赖)。即:componentDidMount时会触发,componentDidUpdate不会触发。

如下代码,由于依赖是空,所以,useEffect只表示componentDidMount。

useEffect( async ()=>{
      let data = await getBooks();  //发送请求的代码已经封装     
      setBooks(data);      
  },[]);

如下代码,表示componentDidMount,和 count变化后引起的componentDidUpdate。

 useEffect( async ()=>{
      let data = await getBooks();  //发送请求的代码已经封装     
      setBooks(data);      
  },[count]);

三、useContext(使用状态树传参)

const value = useContext(context对象);

Context状态树的使用,比较复杂,特别是使用Consumer时。

useContext这个hook能让Context使用起来变得非常简单。不需要再使用Consumer。使用useContext就能拿到context状态树里的值。

useContext函数需要传入一个 context 对象(React.createContext 的返回值),返回该 context 的当前值----由上层组件中距离当前组件最近的 的 value prop 决定。

当组件上层最近的 Provider 更新时,该 Hook 会触发重渲染 。

//context/index.js

import {createContext} from "react";

export default createContext({count:0});

//主入口文件 index.js

import ReactDOM from 'react-dom';
import App from './App';
import Context from "./context"

let count = 10;

ReactDOM.render(
    <Context.Provider value={count}>
     <App/>
    </Context.Provider>,
  document.getElementById('root')
);


//孙子组件 App-->User-->UserAdd.js

import myContext from "../../../context";
import {useContext} from "react";

export default (props)=>{
    let context = useContext(myContext);
    console.log(context);
    return (
        <div>
            <h1>我是用户添加页面</h1>
            <p>count:{context}</p> //此处使用比起consumer是不是简单的多得多得多了呢?
        </div>
    );
}

五、useReducer

是一个min版的redux,不支持中间件redux-thunk。

//reducers.js

export let initState ={
    count:0
}

export let reducer = (state=initState,action)=>{
    let {type,payload} = action;

    switch(type){
        case "ADD":return {
            count:state.count+1
        }
        default:return initState;
    }    
}

//组件里

import {useReducer} from "react";
import {initState,reducer} from "./reducer";

export default ()=>{
    let [state,dispatch] = useReducer(reducer,initState);
    let handleClick = ()=>{
        dispatch({
            type:"ADD"
        });
    }
    return (
        <div>
            <h1>userReducer的使用</h1>
            <p>{state.count}</p>
            <input type="button" value=" 加一 " onClick={handleClick} />
        </div>
    );
}

六、useCallback 记忆函数

useCallBack能干什么?
1、useCallback会返回一个函数的memoized(记忆的)值
2、在依赖不变的情况下,多次定义(如:函数)的时候,返回的值是相同的 。
3.依赖变化就重新调用函数
4.useCallback 防止无效的函数定义

let 新的函数 = useCallback(曾经的函数, [依赖的值])

import {useState,useCallback,memo} from "react"

const MySon = memo((props)=>{
    console.log("props.name",props.name)
    return <input type="button" value={props.name} onClick={props.increament} />
})

export default function Myparent() {
  
    const [p,setP] = useState(0);
    const [name,setName]=useState("hi");
    const [count,setCount] = useState(0);

    //increment1函数是否要重新定义,取决于count的值是否发生变化
    const  increament1 = useCallback(() => {
        console.log('执行increament1')
    },[count])
        
    console.log("父组件更新了");
    return (
      <div>
        <MySon name={name} increament={increament1} />
        {/*点击下面这个按钮,子组件不会刷新,因为,并没有修改count的值*/}
        <input type="button" value="p加一" onClick={e=>setP(p+1)} />
        {/*点击下面这个按钮,子组件会刷新,因为,increment1函数是否要重新定义,取决于count的值是否发生变化,下面这个按钮修改了count的值*/}
        <input type="button" value="count加1" onClick={e=>setCount(count+1)} />
      </div>    
    )
  }

七、useMemo 记忆组件

useMemo(函数,数组);
useMemo防止无效组件的调用 callback防止无效的函数定义

当数组中的其中一个元素,发生变化时,就会调用 函数 。
同时传两个参数,改变一个变都两个都变,期待的是一个变化不影响另一个的变化

import React,{useState,useMemo} from "react";
import './App.css';


function Person({ name, age}) {
    
  console.log("Person函数");
    
  function genName(name) {
    console.log('genName')
    return '姓名:'+name;
  }

   let nameStr = genName(name);  //没有使用useMemo
 // const nameStr =  useMemo(()=>genName(name),[name]) //此处使用  useMemo

  return (
    <>
      <div>{nameStr}</div>
      <hr/>
      <div>年龄:{age}</div>
    </>

  )
}


function App() {
  const [name, setName] = useState('张三疯')
  const [age, setAge] = useState(12)

  return (
    <>
      <button onClick={() => setName("姓名"+age)}>修改姓名</button>
      <button onClick={() => setAge(age+1)}>修改年龄</button>
      <hr/>
      <Person name={name} age={age}></Person>
    </>
  )
}

export default App;

八、useRef 保存引用值 (操作DOM)

useRef 返回一个可变的 ref 对象,其(ref 对象) .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。

import {useRef} from "react";

let refContainer = useRef(initialValue)  

<JSX ref={refContainer} ...

refContainer.current.dom操作

一个常见的用例便是命令式地访问子组件:

function TextInputWithFocusButton() {
    //定义了一个ref变量:inputEl
  const inputEl = useRef(null);
  
  const onButtonClick = () => {
    // `current` 指向已挂载到 DOM 上的文本输入元素
    inputEl.current.focus();
  };
    
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

九、useLayoutEffect同步执行副作用

十、路由相关的hooks

在非路由跳转的组件里,要获取路由上下文对象,除了可以使用高阶组件withRouter外,react-router-dom里还提供了hooks。

1、useHistory(): 返回一个 路由上下文上的history 对象

import { useHistory } from "react-router-dom";

export default (props)=>{
    let history = useHistory();
    console.log("我是用户添加页面:props",props);
    return (
        <div>
            <h1>我是用户添加页面</h1>
            <input type="button" value="跳转" onClick={()=>history.push("/")} />
        </div>
    );
}

2、useLocation():返回一个路由上下文的 location 对象。

<Link to={{pathname:"/User",query:{"id":"01002"}}}>用户管理</Link>

import { useHistory,useLocation } from "react-router-dom";

export default ()=>{
    let location = useLocation();
    let history = useHistory();
    console.log("location",location);
    return (
        <div>
            <h1>我是用户添加页面</h1>
            <input type="button" value="跳转" onClick={()=>history.push("/")} />
        </div>
    );
}

3、useParams():返回当前匹配的路径上的 params

 <Link to={{pathname:"/User/01003"}}>用户管理</Link>

import { useHistory,useLocation,useParams } from "react-router-dom";

export default ()=>{
    let location = useLocation();
    let history = useHistory();
    let params = useParams(); 
console.log("location",location);
console.log("params",params);
return (
    <div>
        <h1>我是用户添加页面</h1>
        <input type="button" value="跳转" onClick={()=>history.push("/")} />
    </div>
);

}

4、useRouteMatch():
可以有一个参数 path,如果什么都不传,会返回当前 context 上的 match 的值,一定是 true。如果传了 path,会比较这个 path 和当前 location 是否 match。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值