React中的Hook

概览

Hook只能在函数组件中调用
在这里插入图片描述

useState

import React , { useState } from 'react'

const App = (params) => {
	const [count,setCount] = useState(0)

	const handleClick = () => {
		setCount(1)
		console.log(count) // 打印: 0 ,说明setCount是异步代码

		// 也可以给setCount赋值一个函数
		setCount((count) => {
			console.log(count) // 打印旧的count
			count++
			console.log(count) // 打印新的count
			return count // 返回最新值覆盖count
		})
		
		setCount(count) // 赋的值和原来相同的话,并不会触发组件的重新渲染
	}
	
	console.log(count) // 打印:1 ,这个位置打印可以即时获取count的最新值	

	return (
		<div>
			<div>{count}</div>	
			<button onClick={handleClick}>按钮</button>
		</div>
	)
}

useEffect && UseLayoutEffect && useInsertionEffect

useLayoutEffect的方法签名和useEffect一样,功能也类似。不同点在于,useLayoutEffect的执行时机要早于useEffect,它会在DOM改变后调用。在老版本的React中它和useEffect的区别比较好演示,React18中,useEffect的运行方式有所变化,所以二者区别不好演示。

useLayoutEffect使用场景不多,实际开发中,在effect中需要修改元素样式,且使用useEffect会出现闪烁现象时可以使用useLayoutEffect进行替换。
在这里插入图片描述
例:

const App = () => {
    const [count, setCount] = useState(1);
    const [show, setShow] = useState(false);
    const h3Ref = useRef();
    const divRef = useRef();

    useEffect(()=>{
        //console.log('useEffect', h3Ref);
        if(!divRef.current) return;

        // 修改样式
        divRef.current.style.marginTop = "200px";
    }, [show]);

    // useLayoutEffect(()=>{
    //     console.log('useLayoutEffect', h3Ref);
    // });

    // useInsertionEffect(()=>{
    //     console.log('useInsertionEffect', h3Ref);
    // });



    return (
        <div>
            <h1>App</h1>
            <h3>{count}</h3>
            <button onClick={()=>setCount(prevState => prevState + 1)}>点我</button>
            <hr/>
            <button onClick={()=>setShow(prevState => !prevState)}>显示/隐藏</button>
            {
                show && <div ref={divRef} style={{width:100, height:100, backgroundColor:"red"}}/>
            }
        </div>


    );
};

React18以下时,我们发现页面的红色块在useEffect中,从出现到下移200像素之间会有明显的闪屏,但是换成useLayoutEffect时,却没有这个问题。

useInsertionEffect通常用于插入元素
useLayoutEffect通常用于布局
useEffect万能(推荐)

useContext

作用:向所有的后代传参

import React, {Component , creatContext} from 'react'

const AppContext = creatContext(null); // 初始值

// 类组件,第一种方式,通过函数消费祖先传过来的value
class Foo extends Component {
	render(){
		return (
		<AppContent.Consumer>
			{value => <div>{values}</div>}
		<AppContent.Consumer>
		)
	}
}

// 类组件,第二种消费祖先传过来的值的方式
class Bar extends Component {
	static contextType = AppContext
	render(){
		const value  = this.context
		return (
			<div>{value}</div>
		)
	}
}


// 以上的两种方式只能用于类组件,函数组件用useContext来消费祖先传过来的值
// 函数组件
const Baz = () => {

	const value = useContext(AppContext)
	
	return (
		<div>{value}</div>
	)
}

const Middle = () => {
	return (
		<div>
			<Foo />
			<Bar />
			<Baz />
		</div>
	)
}

const App = (params) => {
	return (
	<AppContext.Provider value='fanghui'>
		<Middle />	
	<AppContext.Provider>
	)
}

useReducer

作用:处理一些复杂的更加复杂的state运算

import Reac , { useReducer } from 'react'

const App = (params) => {

	const countReducer = (action,state) => {
		switch(action.type){
			case 'add':
				return state + 1
			case 'sub':
				return state - 1
			default:
			   return state
		}
	}
	
	const [count,dispatchCount] = useReducer(0)

	const add = () => {
		dispatchCount({type:'add'})
	}

	const sub = () => {
		dispatchCount({type:'sub'})
	}
	
	return (
		<div>
			<div>{count}</div>	
			<button onClick={add}>1</button>
			<button onClick={sub}>1</button>
		</div>
	)
} 

useMemo 和 useCallback

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

// memo 的作用可以对前后传来的props进行浅比较,如果没有改变则不会重新渲染,如果改变则重新渲染
const Foo = memo(props => {
  console.log("Foo render");
  return (
    <div>
      <ul>{props.render()}</ul>
      {props.count}
    </div>
  )
})

const App = () => {
  const [range, setRange] = useState({ min: 0, max: 10000 });
  const [count, setCount] = useState(0);

  // 返回值render是一个函数,它的引用只会在依赖项发生改变的时候,才会改变
  const render = useCallback(
    params => {
      let list = [];
      for (var i = 0; i < range.max; i++) {
        list.push(<li key={i}>{i}</li>);
      }
      return list;
    },
    [range]
  );

  // 返回值是函数执行后的结果,函数的执行只会在依赖项发生改变的时候,才会再次执行
  // const render = useMemo(
  //   params => {
  //     let list = [];
  //     for (var i = 0; i < range.max; i++) {
  //       list.push(<li key={i}>{i}</li>);
  //     }
  //     return list;
  //   },
  //   [range]
  // );

  //useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

  return (
    <div>
      <h1>{count}</h1>
      <button
        onClick={() => {
          setRange({
            ...range,
            max: range.max + 1
          });
        }}
      >
        add
      </button>
      <Foo render={render}></Foo>
    </div>
  );
}

useRef

提到useRef,我们不得不再提下creatRef

  1. 都可以获取原生DOM
import Reac , { creatRef, useRef } from 'react'

const FuncComp = (params) => {

	// 可以用creatRef创建ref容器,creatRef同样适用于类组件
	const inputRef = creatRef()
	// 也可以用useRef创建ref容器,useRef只能用在函数组件
	const inputRef = useRef()

    focusInput = () => {
		inputRef.current.foucs()
	}

	// 当我们每次更新state时,组件会重新渲染,我们发现通过creatRef创建的ref每次的引用都是不同的
	// 而useRef创建出来的ref的引用都是相同的,我们可以利用这个特性来存一些旧的数据 比如,inputRef.current.value = state.value ,这样即使当state的值被改变时,它存的还是旧值
	// 建议在类组件里使用creatRef,函数组件里使用useRef,这样可以提高性能
	
	return (
		<div>
			<input ref={inputRef} />	
			<button onClick={focusInput}>聚焦</button>
		</div>
	)
} 
  1. useRef和creatRef的区别

当子组件是类组件时

import Reac , { creatRef, useRef,Component } from 'react'

class ChildClassComp extends Component {
	
	const inputRef = creatRef()
	
	focusInput = () => {
		inputRef.current.foucs()
	}
	 
	render(){
		return (
				<div>
					<input ref={inputRef} />	
					<button onClick={focusInput}>聚焦</button>
				</div>
			)
	}
}

class ClassComp extends Component {

	// 当我们用creatRef绑定在类组件身上时,可以获取到类组件的实例
	const childRef = creatRef()

	conponentDidMount(){
		//调用类组件身上的方法
		childRef.current.focusInput()
	}
	
	render(){
		return (
				<div>
					<ChildClassComp  ref={childRef}/>	
				</div>
			)
	}
	
} 

当子组件是函数组件时

import Reac , { creatRef, useRef,Component,forwordRef } from 'react'

const ChildFuncComp = forwordRef ((props,ref) => {
	
	return (
			<div>
				<input ref={ref} />	
				<button onClick={focusInput}>聚焦</button>
			</div>
		)
})

class ClassComp extends Component {

	const childRef = creatRef()

	conponentDidMount(){
		//转发 refs 到子组件 DOM 组件
		childRef.current.focus()

		// ref绑定的子组件是函数组件时,无法像绑定类组件一样直接获取类组件的实例
		// 而且函数组件时不允许绑定ref的,否则控制台会报错,我们需要用forwordRef包裹一下
		// 然后转发ref绑定函数子组件的DOM元素
	}
	
	render(){
		return (
				<div>
					<ChildClassComp  ref={childRef}/>	
				</div>
			)
	}
	
} 

刚学的时候,还是蛮绕的,通过Js++集团的小夏老师的讲解终于弄懂了,感谢 !

总结 :

  1. creatRef创建ref容器,既可以绑定类组件也可以绑定函数组件,但是当函数组件每次重新渲染时,它所创建的ref的引用都是新的,所以我们在函数组件里更推荐使用useRef,它每次创建出来的ref的引用都是一样的,性能更高。而类组件里别无选择,只能使用creatRef,它也只会在类组件里执行一次,ref的引用都是一样的。
  2. 不论是useRef还是creatRef创建出来的ref绑定在类组件身上的时候,获取的是绑定类组件本身的实例,而当绑定的是函数组件的时候,只能使用forwordRef来转发到函数子组件里的具体DOM元素(我记的别的文章也叫穿透ref)

useImperativeHandle

前提:学会这个useImperativeHandle前,你需要学会useRef这个hook

作用:在项目中,别人调用我们写的子组件时,需要知道我们这个子组件所暴露给父组件的实例值,useImperativeHandle 应当与 forwardRef 一起使用

import Reac , { creatRef, useRef,Component,forwordRef } from 'react'

const ChildFuncComp = forwordRef ((props,ref) => {

  const inputRef = useRef()

  useImperativeHandle(ref,() => {
	return {
		customerFocus: () => {
      		inputRef.current.focus();
    	},
    	sayHello: () => {
    		console.log('Hello World')
		}
	}
  })
	
	return (
			<div>
				<input ref={inputRef} />	
				<button onClick={focusInput}>聚焦</button>
			</div>
		)
})

class ClassComp extends Component {

	const childRef = creatRef()

	conponentDidMount(){
		childRef.current.customerFocus()
		childRef.current.sayHello()
	}
	
	render(){
		return (
				<div>
					<ChildClassComp  ref={childRef}/>	
					<button onClick={inputRef}>聚焦</button>
				</div>
			)
	}
	
} 

useDeferredValue

待写…

useTransition

问题:

假设StudentsList是一个比较耗时操作的组件,这时,你在input框输入改变filterWord的值时,你会发现,输入input时非常的卡顿,原因时filterWord被两个组件同时用到,而StudentsList组件的耗时,会影响到input组件在页面中的显示效果,现在,我们想让这两个组件互不影响页面的效果,我们就需要用到useTransition这个hook了

export default const Index = () => {
	
	const [filterWord,setFilterWord] = useState('');


	const changeHander = (e) => {
		setFilterWord(e.target.value)
	}

	return ( 
		<input onClick={changeHander} value={filterWord} />
		<StudentsList filterWord={filterWord} />  // 假设StudentsList是一个比较耗时操作的组件
	)
}

解决:

export default const Index = () => {
	
	// isPending 指示过渡任务何时活跃以显示一个等待状态:true表示正在等待,false表示执行完毕
	const [isPending, startTransition] = useTransition();
	
	const [filterWord,setFilterWord] = useState('');
	const [filterWord2,setFilterWord2] = useState('');

	const changeHander = (e) => {
		setFilterWord(e.target.value)
		
		// startTransition 的回调函数中设置的setState会在其他setState生效后才执行
		startTransition(() => {
			setFilterWord2(e.target.value)
		})
	}

	return ( 
		<input onClick={changeHander} value={filterWord} />
		{!isPending && <StudentsList filterWord={filterWord2} />}  // 假设StudentsList是一个比较耗时操作的组件
	)
}

未完,待续…(可能)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值