React-Hook使用总结

useState

声明 State 变量

import React, { useState } from 'react';

function Example() {
  // 声明一个叫 "count" 的 state 变量 
const [count, setcount] = useState(0);

声明变量的注意事项

  • count和setcount一致(方便代码的后期维护)
  • useState()参数你可以是数字,字符串,数组,对象,箭头函数
import React,{useState}from 'react'

export default function Test01() {
    const [num, setCount] = useState(0);
    const [name, setname] = useState("inno");
    const [arr, setarr] = useState([0,1,2,3,4])
      const [cou] = useState({name:"对象"})
    const [count,setcount] = useState(()=>{
        return 0
    });
    return (
        <div>
            {num}<br/>
            {name}<br/>
            {arr}
            {cou}
            {count}
        </div>
    )
}

读取变量
在函数中,我们可以直接用 count:

  <p>You clicked {count} times</p>

更新state

  <button onClick={() => setCount(count + 1)}>    Click me
  </button>

useState使用规则

  • state设置重复的值—只会重复渲染一次
import React,{useState}from 'react'

export default function Test01() {
    const [count,setcount] = useState(0);
      console.log(count);
    return (
        <div>
        {count} 
        <button onClick={()=>{
            setcount(10)
        }}></button>
        </div>
    )
}

代码分析
//1
count=0
渲染 //count
=0
//2
count=10
渲染//count
=10
//3
count=10
渲染//count
=10

  • useState,useEffect只在最顶层使用 Hook,不要在循环,条件或嵌套函数中调用 Hook
    遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确

React靠的是Hook的调用顺序,来确定那个state对应那个useState

// ------------
// 首次渲染
// ------------
useState('Mary')           // 1. 使用 'Mary' 初始化变量名为 name 的 state
useEffect(persistForm)     // 2. 添加 effect 以保存 form 操作
useState('Poppins')        // 3. 使用 'Poppins' 初始化变量名为 surname 的 state
useEffect(updateTitle)     // 4. 添加 effect 以更新标题

// -------------
// 二次渲染
// -------------
useState('Mary')           // 1. 读取变量名为 name 的 state(参数被忽略)
useEffect(persistForm)     // 2. 替换保存 form 的 effect
useState('Poppins')        // 3. 读取变量名为 surname 的 state(参数被忽略)
useEffect(updateTitle)     // 4. 替换更新标题的 effect

// ...

只要 Hook 的调用顺序在多次渲染之间保持一致,React 就能正确地将内部 state 和对应的 Hook 进行关联。但如果我们将一个 Hook (例如 persistForm effect) 调用放到一个条件语句中会发生什么呢?

// 🔴 在条件语句中使用 Hook 违反第一条规则
  if (name !== '') {
    useEffect(function persistForm() {
      localStorage.setItem('formData', name);
    });
  }

在第一次渲染中 name !== ‘’ 这个条件值为 true,所以我们会执行这个 Hook。但是下一次渲染时我们可能清空了表单,表达式值变为 false。此时的渲染会跳过该 Hook,Hook 的调用顺序发生了改变:

useState('Mary')           // 1. 读取变量名为 name 的 state(参数被忽略)
// useEffect(persistForm)  // 🔴 此 Hook 被忽略!
useState('Poppins')        // 🔴 2 (之前为 3)。读取变量名为 surname 的 state 失败
useEffect(updateTitle)     // 🔴 3 (之前为 4)。替换更新标题的 effect 失败

React 不知道第二个 useState 的 Hook 应该返回什么。React 会以为在该组件中第二个 Hook 的调用像上次的渲染一样,对应的是 persistForm 的 effect,但并非如此。从这里开始,后面的 Hook 调用都被提前执行,导致 bug 的产生。
这就是为什么 Hook 需要在我们组件的最顶层调用。如果我们想要有条件地执行一个 effect,可以将判断放到 Hook 的内部:

 useEffect(function persistForm() {
    // 👍 将条件判断放置在 effect 中
    if (name !== '') {
      localStorage.setItem('formData', name);
    }
  });

useEffect

声明:useEffect(() => { effect return () => { cleanup }; }, [input]);
useEffect作用

  • 推迟代码执行,渲染完成之后再执行 (获取ajax请求,异步获取数据)
import React,{useState, useEffect } from 'react'

export default function Test01() {
    const [data,setdata] = useState(0)
    useEffect(() => 
        setdata(10),[data]
        )
    
console.log("渲染第一次"+data);
    return (
        <div>
       {data}
        </div>
    )
}

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

export default function Test01() {
    const [data,setdata] = useState(0)
useEffect(() => {
    Axios('./a.json').then((res)=>{
     setdata(res.data) 
 }) 
}, [setdata])
console.log("渲染第一次"+data);
    return (
        <div>
       {data}
        </div>
    )
    }

//第1次渲染
data==>0
渲染第一次:0
useEffect中的setdata触发重新渲染
//第2次渲染
data==>[inno,blue]
渲染第2次:[inno,blue]
useEffect中的setdata触发重新渲染
//第3次渲染
data==>[inno,blue]
渲染第3次:[inno,blue]
useEffect中的setdata触发重新渲染
两次state值一样,触发useState
的bailing out 机制。

  • 完成移除 (定时器,监听)
import React, { useState ,useEffect} from 'react'

export default function Test01() {
    const [count, setcount] = useState(0)
    useEffect(() => {
    let timer=setInterval(() => {
        setcount(count+1)
    }, 1000);
        return () => {
        clearInterval(timer)
        }
    })
    return (
        <div>
           {count} 
        </div>
    )
}
  • 监听与跳过执行
import React, { useState,useEffect } from 'react'

export default function Test01() {
    const [count, setcount] = useState(0)
    const [num, setnum] = useState(0)
    useEffect(() => {
      let timer= setInterval(() => {
            setnum(num+10)
        }, 1000);
        return () => {
        clearInterval(timer)
        }
    }, [num])
useEffect(() => {
    
   
}, [count])
    return (
        <div>
        {num}<br/>
        {count}<br/>
        
           <button onClick={()=>{setcount(count+1)}}></button> 
        </div>
    )
}

  • useEffect和useState盲目的结合使用时,常方法内存泄露

useRef

声明:const txt = useRef()
标记:<input ref={txt} defaultValue="asd"></input>
读取:txt.current.value

import React,{useRef,useEffect} from 'react'

export default function Test01() {
const txt = useRef()
useEffect(() => {
   console.log(txt.current.value);
}
    )
    return (
        <div>
            <input ref={txt} defaultValue="asd"></input>
        </div>
    )
}

forwardref

传递引用
函数组件不像class组件一样,没有实例,因此父组件想要获取子组件的值,需要forwardref的帮助

import React ,{useRef, useEffect}from 'react'
import Nav from './nav'

export default function Test01() {
    const txt = useRef()
    useEffect(() => {
        console.log(txt.current.value);
    })
    return (
        <div>
            <Nav ref={txt}/>
        </div>
    )
}

import React ,{forwardRef}from 'react'

export default forwardRef((props,ref)=> {
    return (
        <div>
            <input ref={ref} defaultValue="sf"></input>
        </div>
    )
}
)

useContext

创建context对象:

const ThemeContext = React.createContext(defaultValue)

使用context对象

  <ThemeContext.Provider value={themes.dark}>
      <Toolbar />
    </ThemeContext.Provider>

获取context对象的value

 const theme = useContext(ThemeContext);
import React,{useContext} from 'react'
const themes = {
    light: {
      foreground: "#000000",
      background: "#eeeeee"
    },
    dark: {
      foreground: "#ffffff",
      background: "#222222"
    }
  };
  
  const ThemeContext = React.createContext(themes.light);
  
  export default  function Test01() {
    return (
      <ThemeContext.Provider value={themes.dark}>
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
  
  function Toolbar(props) {
    return (
      <div>
        <ThemedButton />
      </div>
    );
  }
  
  function ThemedButton() {
    const theme = useContext(ThemeContext); 
    console.log(theme);
     return (   
          <button style={{ background: theme.background, color: theme.foreground }}>   
             I am styled by theme context!   
              </button> 
               );
  }
 
  

useReducer

声明:
const [state, dispatch] = useReducer(reducer, initialState, init)
reducer:根据action.type返回state
(state, action) => newState
initialstate:初始化state

const initialState = {count: 0};

加数器案例

import React,{useReducer} from 'react'

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}
export default function Test01() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

useMemo

声明:const memoizedValue = useMemo(() => function, [a, b]);
memoizedValue:usememo保存渲染的结果
() => function:函数作为参数
[a, b]:依赖项数组作为参数
原理:useMemo是解决react性能优化的问题,只有当依赖项数组发生改变时参数函数才会去执行
案例:点击a,child1发生渲染,b不会。点击b,child2发生渲染,child1不会。

import React, { useState, useMemo } from 'react'
const Child=({a})=>{
  return <h2>{a}</h2>
}
function Parent({a,b}){
  const child1=useMemo(() => {
console.log("这里a是child1渲染");
return  <div>
 <Child a={a}/>
  </div>}, [a])
 const child2=useMemo(() =>{  
  console.log("这里b是child2渲染")
  return <div>
<Child a={b}/> </div>}, [b])
return <React.Fragment>{child1},{child2}</React.Fragment>
}

export default function Test01() {
  const [a, seta] = useState(0)
  const [b, setb] = useState(0)
  
 
  return (
    <div>
    <Parent a={a} b={b}></Parent>
    {a}<br/>
    {b}
    <button onClick={()=>{seta(a+1)}}>a</button>
    <button onClick={()=>{setb(b+1)}}>b</button>
    </div>
  )
}

自定义HOOK

自定义的hook实质就是平时函数中我们提取封装函数,只是自定义的函数名必须是use开头。
另外由于hook本身就是函数,所以他们之间可以传递信息。

hook+redux

获取数据

 const state = useSelector(state => state.state)

发送action

 const dispatch = useDispatch(function)
  **useSelector替代mapStateToProps
    useDispatch替代mapDispatchToProps
    同时将action写成方法

**

案例
子组件:

import React from "react";
import { useSelector, useDispatch } from "react-redux";

const Toggle = () => {
  const go = useSelector(state => state.go);
  const dispatch = useDispatch();
  const asyncfunc = () =>{
    setTimeout(()=>{
      dispatch({ type: 'toggle' })
    },1000)
  }
  return (
    <div>
      <div>{JSON.stringify(go)}</div>
      <input
        type="checkbox"
        value={go}
        onChange={() => asyncfunc()}
      />
    </div>
  );
};

export default Toggle;

store的创建文件 【无需再使用中间件,因为异步调用已解决 只需创建store即可】

//store.js
import {createStore} from 'redux'
import reducer from './reducer'

export default createStore(reducer)

在需要使用redux的最大组件进行 引入与包装 【不变】

import {Provider} from 'react-redux'
import store from './store' 			//此为store

import Toggle from './toggle';		//此为组件

render(
  <Provider store={store}>
    <Toggle/>
  </Provider>,
  document.getElementById('root')
);

总结:
去掉 - 子组件入口 【即书写中间件层】
增加 + 子组件的引入{ useSelector, useDispatch }
减少 - ./store.js创建时嵌套中间件

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值