react-hooks


参考笔记: 链接

规则:

只在最顶层使用Hook;
只在React组件中才能调用。

useState

一般来说,在函数退出后变量就会”消失”,而 state 中的变量会被 React 保留
还可以用下面这种方法来设置state

setcount(()=>{
number+=1
return number
})

useEffect

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

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);
  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }
  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {      // 清除副作用( effect 的清除阶段在每次重新渲染时都会执行,而不是只在卸载组件的时候执行一次。)
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  },[]);    // 设置依赖,当这个依赖的变化了,就会重新执行useEffect,如果传入一个空数组,就只会在挂载阶段执行一次
  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

传入回调函数,且这个函数的返回值是一个函数,同时传入一个空数组。假如回调函数本身记为 A, 返回的函数记为 B,那么将在挂载阶段执行 A,卸载阶段执行 B
这里B函数的角色定位就类似于生命周期里 componentWillUnmount 方法里的逻辑
useEffect(()=>{
// 这里是 A 的业务逻辑
// 返回一个函数记为 B
return ()=>{
}
}, [])

useRef

获取DOM元素对象

import { useRef } from 'react'
function App () {
    const username = useRef()
    const handler = () => console.log(username) // { current: input}
    return <input ref={username} onChange={handler} />
}

exporrt default App

操作组件

  • 函数组件获取不了ref
  • 给类组件绑定ref,获取组件的属性
    • 给函数组件绑定ref,必须使用forwardRef 包裹,并且去获取子组件绑定ref的对象
import { forwardRef ,useRef} from "react";
//forwardRef获取子组件的Dom
const Forward = forwardRef((props,ref)=>{
    return (
        <div>
            <div ref={ref}>富婆的快乐你不懂</div>
        </div>
    )
})
function App() {
    const el = useRef(null)
    return (
        <div className="App">
            <Forward ref={el}></Forward>
            <button onClick={()=>{
                console.log(el.current);
            }}>获取子组件的Dom</button>
        </div>
    );
}

ReactDOM.render(<App />, document.getElementById("root"));

createContext && useContext 多层传递

用于向后代组件传递数据

外层组件

import React, { createContext } from 'react'
import ChildrenCom from "./childrenCom"
export const countContext = createContext();

export default function Home() {
    return (
        <countContext.Provider value={100}>
            <h1>
                context
            </h1>
            <ChildrenCom>
            </ChildrenCom>
        </countContext.Provider>
    )
}

后代组件

import { useContext } from 'react'
import { countContext } from "../index"

export default function Home() {
    const value = useContext(countContext);
    return (
        <>
            <h1>
                suncom--{value}
            </h1>
        </>
    )
}

useMemo()

作用: 类似于Vue中的计算属性,可以监测某个值的变化,根据变化值计算新值。
useMemo会缓存计算结果,如果监测值没有发生变化,即使组件重新渲染,也不会重新计算,此行为可以有助于避免在每个渲染上进行昂贵的计算。

import React, { useState, useMemo } from 'react'

function App() {
    const [count, setCount] = useState(0)
    const result = useMemo(() => {
        // 如果count值发生变化此函数重新执行
        return count * 2
    }, [count])
    return (
        <div>
            <span>{count} {result}</span>
            <button onClick={() => setCount(count + 1)}>+1</button>
        </div>
    )
}

export deefault App

useCallback && memo

主要是用于子组件修改父组件的值
useCallback 作用:性能优化,缓存函数,使用组件重新渲染时得到相同的函数实例。从而避免嵌套函数的再次渲染。
memo 作用:性能优化,如果本组件的数据没有发生变化,阻止组件更新

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

function App() {
    const [count, setCount] = useState(0)
    const resetCount = useCallback(() => setCount(0), [setCount])
    return (
        <div>
            <span>{count}</span>
            <button onClick={() => setCount(count + 1)}>+1</button>
            <Foo resetCount={resetCount} />
        </div>
    )
}

const Foo = memo(function Foo(props) {
    // memo包裹后,count发生变化,App组件重新渲染,但是子组件中的数据并未发生变化所以Foo不会被重新渲染
    console.log("Foo组件重新渲染了")
    return (
        <div>
            我是Foo组件
            <button onClick={props.resetCount}>resetCount</button>
        </div>
    )
})

export default App

自定义hook并使用

在src下新建 hooks,hooks下新建 usePosition.jsx

  • 必须使用use开头
import react,{useState,useEffect} from "react"

export default function usePosition(){
    const[position,setPosition]=useState({x:0,y:0})
    useEffect(()=>{
        function getPosition(event){
            setPosition({x:event.clientX,y:event.clientY})
        }
        document.addEventListener("click",getPosition)
        return ()=>{
            document.removeEventListener("click",getPosition)
        }
    })
    return {
        position,setPosition
    }
}

组件内使用

import react,{useState,useEffect} from "react"

import usePosition from "../../hooks/usePosition"
export default ()=>{
    const [number,setNumber]=useState(0)
    useEffect(()=>{
        document.title=`当前的number值为${number},我在第一次渲染和每次页面更新时都会执行`
    })
    const {position,setPosition } =usePosition()
    return (
        <div>
            <div>
                number---{number}
            </div>
            <div>
                {position.x}---{position.y}
            </div>
            <button onClick={()=>{setNumber(number+1)}}>
               +1 
            </button>
        </div>
    )
}

hooks下处理表单(处理受控组件)

import React, { useState } from "react";
import "./styles.css";
function Form() {
  const [firstName, setFirstName] = useState("");
  const [lastName, setLastName] = useState("");
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  return (
    <form>
      <input
        value={firstName}
        onChange={e => setFirstName(e.target.value)}
        placeholder="First name"
        type="text"
        name="firstName"
        required
      />
      <input
        value={lastName}
        onChange={e => setLastName(e.target.value)}
        placeholder="Last name"
        type="text"
        name="lastName"
        required
      />
      <input
        value={email}
        onChange={e => setEmail(e.target.value)}
        placeholder="Email address"
        type="email"
        name="email"
        required
      />
      <input
        value={password}
        onChange={e => setPassword(e.target.value)}
        placeholder="Password"
        type="password"
        name="password"
        required
      />
      <button type="submit">Submit</button>
    </form>
  );}
export default Form;

表单数据收集示例

import { useState } from "react"
import "./index.css"
export default function Form(){
    const [username,setUsername]=useState("")
   
   async function handleForm(e){
        e.preventDefault()
      	// 建议使用 ref 来获取
        let dizhi=radioCheck(document.querySelectorAll(".dizhi"))
        let xins=chk(document.getElementsByName('xin'))
        let renwu=document.querySelector("#renwu").value
        let upfile=await collectImg(document.querySelector("#fileUp"))
        console.log(upfile)
        // console.log({username,dizhi,xins,renwu})
    }
     // 收集单选框数据
     function radioCheck(ele){ 
        let radios=ele
        let value = "";
        for(let i=0;i<radios.length;i++){
            if(radios[i].checked == true){
                value = radios[i].value;
            }
        }
        return value
    } 
    // 收集复选框数据
    function chk(ele){ 
        let obj=ele; 
        let s=''; 
        for(let i=0; i<obj.length; i++){ 
            if(obj[i].checked) s+=obj[i].value+','; 
        } 
       
        return s
    } 
    // 收集上传的图片信息
    function collectImg(ele1) {
        return new Promise((res, rej) => {   
            var v = ele1.value;
            var reader = new FileReader();
            reader.readAsDataURL(ele1.files[0]);
    
            // 获取原来文件名
            let oldfilename = ele1.files[0].name || ''
         
            let strs = v.split('.');
            var suffix = strs[strs.length - 1];
    
            // 判断文件格式
            if (suffix != 'jpg' && suffix != 'gif' && suffix != 'jpeg' && suffix != 'png') {
                alert("你选择的不是图片,请选择图片!", 'error');
                var obj = document.getElementById('fileUp');
                obj.outerHTML = obj.outerHTML; //这样清空,在IE8下也能执行成功
                return false
            }
            // 判断文件的大小 小于2M
    
            if (ele1.files[0].size / 1000 > 2 * 1024) {
                alert("上传文件不能大于2M", 'error')
                return false
            }
    
            reader.onload = function (e) {
                // if (pre) {
                //     if (ele2 !== null) {
                //         $(ele2).hide()
                //     }
    
                //     $(ele3).empty();
                //     let img1 = `
                //         <img class="userheader" src="${e.target.result}" alt="">`
                //     $(ele3).append(img1)
                // }
    
                res({
                    oldfilename,
                    imgdata:e.target.result
                    
                })
            };
    
        })
    }
    return (
        <form action="#">
            <label htmlFor="username" className="item">
            <input type="text" value={username} onChange={e=>setUsername(e.target.value)} name="username" id="username" placeholder="username" />
            </label>
		
            <div className="item">
                <input type="radio" name="dizhi" value="广州"  className="dizhi" />广州
                <input type="radio" name="dizhi" value="上海" className="dizhi" />上海
                <input type="radio" name="dizhi" value="北京"  className="dizhi" />北京
            </div>
            <div className="item">
                <input type="checkbox" name="xin" id="" value="李" className="xin" /><input type="checkbox" name="xin" id="" value="张" className="xin" /><input type="checkbox" name="xin" id="" value="魏" className="xin" /></div>
            <div className="item">
			// 这儿 也可以 使用上面的 username 方式来获取
              <select name="renwu" id="renwu">
                  <option value="">
                      请选择
                  </option>
                  <option value="东邪">
                      东邪
                  </option>
                  <option value="西毒">
                      西毒
                  </option>
              </select>
            </div>
            <div className="item">
                <input type="file" name="" id="fileUp" />
            </div>
            <button onClick={handleForm}>提交</button>
        </form>
    )
}

其他Hook

useReducer()

作用:另一种让函数组件保存状态的方式
使用细节:和redux的使用类似

import React, { useReducer } from 'react'

function App() {
    function reducer (state, action) {
        switch (action.type) {
            case 'increment':
                return state + 1;
            case 'decreament':
                return state - 1;
            default:
                return state;
        }
    }
}

const [ count, dispatch ] = useeReducer(reducer, 0)
return (
    <div>
        <button onclick={() => dispatch({type: 'decrement'})}>-1</button>
        <span>{count}</span>
        <button onclick={() => dispatch({type: 'increment'})}>-1</button>
    </div>
)

export default App;

useCallback()

作用:性能优化,缓存函数,使用组件重新渲染时得到相同的函数实例。从而避免嵌套函数的再次渲染。

使用场景

  • 父组件向子组件传递函数,使用useCallback,子组件使用memo
import React, { useState, memo, useCallback } from "react"

function App() {
    const [count, setCount] = useState(0)
    const resetCount = useCallback(() => setCount(0), [setCount])
    return (
        <div>
            <span>{count}</span>
            <button onClick={() => setCount(count + 1)}>+1</button>
            <Foo resetCount={resetCount} />
        </div>
    )
}

const Foo = memo(function Foo(props) {
    // memo包裹后,count发生变化,App组件重新渲染,但是子组件中的数据并未发生变化所以Foo不会被重新渲染
    console.log("Foo组件重新渲染了")
    return (
        <div>
            我是Foo组件
            <button onClick={props.resetCount}>resetCount</button>
        </div>
    )
})

export default App

useImperativeHandle

useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值。在大多数情况下,应当避免使用 ref 这样的命令式代码。useImperativeHandle 应当与 forwardRef 一起使用:

useLayoutEffect

其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。
尽可能使用标准的 useEffect 以避免阻塞视觉更新。

其他

rmc // 快速创建组件(前提要先安装插件)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值