React hooks 篇 useRef 与 useContext

useRef

useContext 


 

useRef 获取dom元素 & 注册抛出组件方法数据 & 静态的跨渲染时机的状态 

const initialState = 0;
const iref = useRef(initialState);

返回一个可变的 ref 对象,该对象只有个 current 属性,初始值为传入的参数( initialState )。

只会在初始化的钩子生成一次

当current的值发生改变时并不会触发render

可以跨渲染时机拿到值

获取操作dom元素

import React, { useRef } from "react";

const RefDomFiber = () => {

  const inputRef = useRef<HTMLInputElement>(null);

  return (
    <div>
      <input type="text" ref={inputRef} />
      <button onClick={() => {
        inputRef.current?.focus();
      }}>input聚焦</button>
    </div>
  );
};

export default RefDomFiber;

注册一个ref,给input绑定上去,然后进行操作

可以跨渲染时机拿到值

当定义了一个useState的状态,给定一个延迟器然后console打印,在这期间进行数据state修改,在console打印时拿到的值依旧是延迟器之前的值。

const [num, setNum] = useState(0)
function clickhandle() {
  console.log('触发了打印事件')
  setTimeout(() => {
    console.log(`num的值: ${num}`);
  }, 3000);
}
return (
  <>
    <button onClick={() => {
      setNum(num + 1);
      console.log('num当前的值: ', num + 1)
    }}>num: {num}</button>
    <button onClick={clickhandle}>打印</button>
  </>
);

 

 原因: react每次render时,都会重新注册一遍函数,当点击打印的时候方法内部记录了当前这次render对应num的值,延迟期间修改num的值,之前的函数是不会拿到新的num

const iref = useRef(0);
const [num, setNum] = useState(iref.current)
function clickhandle() {
  console.log('触发了打印事件')
  setTimeout(() => {
    console.log(`iref的值: ${iref.current}`);
  }, 3000);
}
return (
  <>
    <button onClick={() => {
      iref.current += 1
      setNum(iref.current)
      console.log('iref当前的值:', iref.current)
    }}>iref的值:{iref.current}</button>
    <button onClick={clickhandle}>打印</button>
  </>
);

 这里注册了一个state:num,还注册了一个ref:iref,因为ref的修改是不会触发render,所以在修改的时候把state也修改了让render触发,useRef相当于全局定义了一个数据,不同时机render注册的函数记录的都是同一个位置的ref

注册调用组件方法

1. 用forwardRef方法注册一个子组件,将父类的ref作为参数传入函数式组件中

2. 用useImperativeHandle给ref 抛出对应的属性方法

import React, { useRef, useState, forwardRef, useImperativeHandle } from "react";

const RefFiber = () => {
  const iref = useRef<{
    getParams?: InitialStateProps;
    setParams?: (_props: InitialStateProps) => void;
  }>();

  const getChildValue = () => {
    console.log('子组件数据:', iref?.current?.getParams)
  }
  
  const updataChildValue = () => {
    iref?.current?.setParams?.({
      id: 2,
      name: '父组件修改',
      title: '父组件修改'
    })
  }
  return (
    <>
      <ChildFiber ref={iref} />
      <button onClick={getChildValue}>获取子组件数据</button>
      <button onClick={updataChildValue}>修改子组件数据</button>
    </>
  );
};

export default RefFiber;

interface InitialStateProps {
  id: number;
  name: string;
  title: string;
}

const initialState = {
  id: 1,
  name: '子组件',
  title: 'childFiber',
}

const ChildFiber = forwardRef((props, ref) => {
  const [state, setState] = useState(initialState);

  useImperativeHandle(ref, () => ({
    getParams: state,
    setParams: (_props: InitialStateProps) => {
      setState(_props);
    }
  }))

  return (
    <div>
      <div>id: { state.id }</div>
      <div>name: { state.name }</div>
      <div>title: { state.title }</div>
    </div>
  )
})

useContext 状态共享器

在开发中通常会遇到一个组件的state状态在多个子组件中传递的共用,如果一级一级的传递非常麻烦,useContext就是解决这个问题这个问题的

1. 首先要要在组件外部创建一个Context

import React, { useState, useContext, createContext } from "react";

interface InitialStateType {
  id: number,
  name: string,
}

const initialState = {
  id: 1,
  name: '1',
}

interface ContextType extends InitialStateType { setState: (value: InitialStateType) => void } 

const AppContext = createContext<Partial<ContextType>>({})

先用createContext创建一个context,为什么要在组件外部创建,因为组件内部state修改都会重新render的把函数内部的东西重新注册一次,如果在组件内部创建就会导致每次render都生成一个新的context。

这里我给createContext传的类型是我用例上需要的,不要被误导

2. 在父组件使用Context的Provider包裹住需要共享状态的组件

const ContextFiber = () => {
  const [state, setState] = useState(initialState);

  return (
    <div>
      <AppContext.Provider value={{ ...state, setState }}>
        <Child1 />
      </AppContext.Provider>
    </div>
  )
};

使用实例的Context.Provider包裹住需要共享状态的组件,用value属性传递需要共享的状态及方法

3. 在子组件中用useContext生成对应的context

const Child1 = () => {
  const contextValue = useContext(AppContext);
  return (
    <div>
      Child1 <br />
      <div>id: { contextValue?.id }</div>
      <div>name: { contextValue?.name }</div>
      <button onClick={() => {
        contextValue?.setState?.({
          id: 2,
          name: '2',
        })
      }}>修改context</button>
    </div>
  )
}

使用useContext传入需要关联的context,返回的数据就会包裹父组件value里面的状态及方法

整体代码

import React, { useState, useContext, createContext } from "react";

interface InitialStateType {
  id: number,
  name: string,
}

const initialState = {
  id: 1,
  name: '1',
}

interface ContextType extends InitialStateType { setState: (value: InitialStateType) => void } 

const AppContext = createContext<Partial<ContextType>>({})

const ContextFiber = () => {
  const [state, setState] = useState(initialState);

  return (
    <div>
      <AppContext.Provider value={{ ...state, setState }}>
        <Child1 />
      </AppContext.Provider>
    </div>
  )
};

export default ContextFiber;

const Child1 = () => {
  const contextValue = useContext(AppContext);
  return (
    <div>
      Child1 <br />
      <div>id: { contextValue?.id }</div>
      <div>name: { contextValue?.name }</div>
      <button onClick={() => {
        contextValue?.setState?.({
          id: 2,
          name: '2',
        })
      }}>修改context</button>
    </div>
  )
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值