函数化组件Hook&组件跨层级通信Context

Hook

Hook是React16.8的一个新增项,可以在不编写class的情况下使用state以及其他的React特性.

useState

从react中导出useState函数

import React,{useState} from 'react';

函数化组件Hook&组件跨层级通信Context_第1张图片
useState(initialState),接收初始状态,返回一个状态变量和其更新函数.在更新函数中传入新的值可以变更状态的值.

useEffect

函数化组件Hook&组件跨层级通信Context_第2张图片
useEffect会在每次渲染时执行,可以有多个useEffect函数.

useEffect(() => {
// Update the document title using the browser API
document.title = `您点击了 ${count}`;
},[]);

可以传递第二个参数为空数组[],这样就只会执行一次类似于componentDidMount

useEffect(() => {
// Update the document title using the browser API
document.title = `您点击了 ${count}`;
},[count]);

第二个参数可以在数组中传入状态,这样useEffect只有在数组中的状态的值发生变化时才会执行.数组中可以有多个状态

useEffect(()=>{
	return () => {};
});

useEffect每次渲染都有独立的状态(State)

每当用户点击一次按钮 都会重新触发render函数,每次render拿到的都是独立的状态
因为我们生命count的值时使用const,所以每次渲染拿到的count值是一个独立的常量

function Demo() {
  const initCount = 0
  const [count, setCount] = useState(initCount)
  return (
    <div>
      <h2>{count}</h2>
      <button onClick={() => setCount(count + 1)}>count++</button>
    </div>
  ) 
}

useEffect每次渲染都有不同且独立的函数(Effect函数)

每次count值改变,都会触发render,组件重新渲染,所以每次都会生成对应的useEffect函数

而且我们发现每次打印count的值拿到的都是当前轮次的count值(并不是最新的count)

function Demo() {
  const initCount = 0
  const [count, setCount] = useState(initCount)
  
  // 假设在1s内多次点击按钮 这里打印的count值是什么?
  useEffect(() => {
    setTimeout(() => {
      console.log(count) // 这里打印的会是当前这一次的count值,并不是最新的count值
    }, 1000)
  })
  
  return (
    <div>
      <h2>{count}</h2>
      <button onClick={() => setCount(count + 1)}>count++</button>
    </div>
  ) 
}

我们知道每次渲染都会触发render,每次更新就会生成一个新的Effect函数,并且每一个Effect函数里面都有独立的State,且只能访问自己本次更新的State。
所以用上面的例子,得出的结论就是:count值其实不是在同一个Effect函数里面发生改变,而是每一次的组件更新,都会生成一个维护着本次更新的Effect函数,在这个最新的Effect函数里就可以访问到最新的count值。

useEffect返回的函数是如何进行清理工作的

function Demo() {
  const initCount = 0
  const [count, setCount] = useState(initCount)
  
 
  useEffect(() => {
    let timer = setTimeout(() => {
      console.log(count)
    }, 1000)
    
    // 清理工作
    return () => {
      clearTimeout(timer)
    }
  })
  
  return (
    <div>
      <h2>{count}</h2>
      <button onClick={() => setCount(count + 1)}>count++</button>
    </div>
  ) 
}

假设用户点击了两次次按钮 当第一次点击的时候 count + 1 = 1,然后执行clearTimout清除本次的定时器?
接着继续count + 1 = 2 然后执行clearTimeout清除本次的定时器?

正确的顺序应该是:当第一次点击 count + 1 = 1,然后clearTimeout会被延迟执行,等到第二次点击的时候 count +1 = 2 再执行上一次的clearTimeout 然后以此类推…问题来了不是说effect函数只能访问本次的State吗?那它怎么拿到上一次的clearTimeout并执行的?

其实很简单,就是React会帮你记住每次effect函数的State(包括清除函数),它确实是只能读取本次更新的State,只不过是延迟执行了(把清除函数的执行时机放在DOM渲染完成后,在下一次render触发之前)

自定义钩子-custom Hook

自定义Hook是一个函数,名称用"use"开头,函数内部可以调用其他钩子

function useAge(){
	const [age,setAge] = useState(0);
	useEffect(()=>{
		setTimeout(()=>{
			setAge(20);
		},2000);
	});
	return age;
}
export default function HooksTest() {
// useState(initialState),接收初始状态,返回一个状态变量和其更新函数
const age = useAge();
return (
<div>
<p>{age?age:"loading..."}</p>
</div>
);
}

其他Hook

useContext,useReducer,useCallback,useMemo

组件跨层级通信 - Context

上下文提供一种不需要每层设置props就能跨多级组件传递数据的方式

Context相关API

  • React.createContext
  • Context.Provider
  • Class.contextType
  • Context.Consumer
import React, { useContext } from "react";

// 1.创建上下文
const MyContext = React.createContext();
const { Provider, Consumer } = MyContext;

function Child(prop) {
  return <div>Child: {prop.foo}</div>;
}
// 使用hook消费
function Child2() {
  const context = useContext(MyContext);
  return <div>Child2: {context.foo}</div>;
}
// 使用class指定静态contextType
class Child3 extends React.Component {
    // 设置静态属性通知编译器获取上下文中数据并赋值给this.context
    static contextType = MyContext;
    render() {
        return <div>Child3: {this.context.foo}</div>
    }
}
export default function ContextTest() {
  return (
    <div>
      <Provider value={{ foo: "bar" }}>
        {/* 消费方法1:Consumer */}
        <Consumer>{value => <Child {...value} />}</Consumer>
        {/* 消费方法2:hook */}
        <Child2 />
        {/* 消费方法3:contextType */}
        <Child3 />
      </Provider>
    </div>
  );
}

你可能感兴趣的:(函数化组件Hook&组件跨层级通信Context)