Hooks笔记

119 阅读3分钟

1. useState

1.1 useState的用法,以及简单实现

import React from 'react';
import ReactDOM from 'react-dom';

// import { useState, useRef } from 'react'
let hookStates = [];
let hookIndex = 0;
function useState(initialState) {
  if (typeof initialState === 'function') {
    hookStates[hookIndex] = hookStates[hookIndex] || initialState();
  } else {
    hookStates[hookIndex] = hookStates[hookIndex] || initialState;
  }
  // currentIndex 闭包,setState中用到currentIndex,防止被垃圾回收
  let currentIndex = hookIndex;
  function setState(newState) {
    if (typeof newState === 'function') {
      newState = newState(hookStates[currentIndex])
    }
    hookStates[currentIndex] = newState;
    render();
  }
  return [hookStates[hookIndex++], setState]
}

function App() {
  // const [number, setNumber] = useState(0);
  const [{number}, setNumber] = useState(() => ({number: 0}));
  const [name, setName] = useState('jeffywin');
  return (
    <>
      <p>{number}</p>
      <p>{name}</p>
      <button onClick={() => setNumber({number: number + 1})}>+</button>
      <button onClick={() => setName(name + 's')}>changeName</button>
    </>
  );
}
function render() {
  hookIndex = 0;
  ReactDOM.render(
      <App />,
    document.getElementById('root')
  );
}

render();

2. uesRef

2.1 useRef的用法

  • useRef一般用在获取最新的值,以及操作dom元素
  • useRef会返回一个可变的ref对象 {current}
  • ref对象在组件的整个生命周期内保持不变
function Counter(){
  let [number,setNumber] = useState(0);
  let lastNumberRef = useRef(number);
  let alertNumber = ()=>{
    setTimeout(() => {
      alert(lastNumberRef.current);
    }, 3000);
  }
  // 在每次渲染结束后,这个时候number值是最新的,就给number赋值
  // 如果只用setNumber,当有延迟时不能获取最新的值
  React.useEffect(()=>{
    //为什么是可变的,因为lastNumberRef.current是可以改的,不是只读的
    lastNumberRef.current = number;
  });
  return (
    <div>
      <p>{number}</p>
      <button onClick={()=>{
        setNumber(number+1);
      }}>+</button>
      <button onClick={alertNumber}>alert</button>
    </div>
  )
}
function render(){
  hookIndex = 0;
  ReactDOM.render(
    <Counter />,
    document.getElementById('root')
  );
}
render();

2.2 useRef的实现

  • useRef的实现比较简单,直接把current传过去
function useRef(current) {
  hookStates[hookIndex] = hookStates[hookIndex]||{current};
  return hookStates[hookIndex++];
}

2.3 forwardRef是什么,为什么需要,怎么实现

  • forwardRef 将ref从父组件转发到子组件的dom元素上,子组件接收props和ref
  • 为什么需要forwardRef,因为函数组件没有实例,父无法直接给子传 ref,而类组件是可以直接传的
  • 通过给子组件包装forwardRef, React.forwardRef(FunctionChild) 就可以达到传递ref的目的
  1. forwardRef怎么用, 例子,获取input焦点
function FunctionChild(props, ref) {
  return <input ref={ref} />; // 最原始的写法,结合改变value
}

// 类组件
class ClassChild extends React.Component {
  constructor(props) {
    super(props);
    this.inputRef = React.createRef();
  }
  render() {
    return <input ref={this.inputRef} />;
  }
}
// 用forwardRef包装子组件
const ForwardFunctionChild = React.forwardRef(FunctionChild);
// 父组件
function Parent() {
  let [number, setNumber] = React.useState(0); //定义一个状态
  const functionChildRef = React.useRef(); //生成一个ref对象 {current:null}
  // const classChildRef = React.useRef(); // 类组件用
  const getFocus = () => {
    // classChildRef.current.inputRef.current.focus(); // 类组件
    functionChildRef.current.focus(); // 函数组件
  };
  return (
    <div>
      {/* 把useRef传给经过forwardRef包裹后的子组件 */}
      <ForwardFunctionChild ref={functionChildRef} />
      <ClassChild ref={classChildRef} />
      <p>{number}</p>
      <button onClick={() => setNumber((x) => x + 1)}>+</button>
      <button onClick={getFocus}>获得焦点</button>
    </div>
  );
}
function render() {
  hookIndex = 0;
  ReactDOM.render(<Parent />, document.getElementById("root"));
}
render();
  1. 简单实现
// 转成类组件
function forwardRef(FunctionChild){
   return class extends React.Component{
     render(){
       //element {type,props,ref,key}
       //ref非常的特殊,不能让直接用,是一个内部保持的变量
       //return FunctionChild(this.props,this.props.ref2);
       return <FunctionChild {...this.props}/>
     }
   }
} 

2.4.useImperativeHandle是什么,怎么用,以及怎么实现

  • functionChildRef.current 这是真实DOM, 如果不做保护,就可以做很多危险的操作
function FunctionChild(props, ref) {
  let inputRef = React.useRef(); //return {current:null}
  useImperativeHandle(ref, () => ({
    focus() {
      inputRef.current.focus();
    },
  }));
  //当这个虚拟的input组件在挂载到页面中之后会ref.current=真实DOM
  //我希望可以控制上级组件的操作
  return <input ref={inputRef} />;
}
const ForwardFunctionChild = React.forwardRef(FunctionChild);
function Parent() {
  let [number, setNumber] = React.useState(0); //定义一个状态
  const functionChildRef = React.useRef(); //生成一个ref对象 {current:null}
  const classChildRef = React.useRef();
  const getFocus = () => {
    //functionChildRef.current 这是真实DOM, 如果不做保护,就可以做很多危险的操作    functionChildRef.current.focus(); // 函数组件
    // 保护后 functionChildRef.current 指向 () => ({ focus() ...})
    // functionChildRef.current.value = "很危险";
  };
  return (
    <div>
      {/* 把ref传给经过forwardRef包裹后的子组件 */}
      <ForwardFunctionChild ref={functionChildRef} />
      <ClassChild ref={classChildRef} />
      <p>{number}</p>
      <button onClick={() => setNumber((x) => x + 1)}>+</button>
      <button onClick={getFocus}>获得焦点</button>
    </div>
  );
}
function render() {
  hookIndex = 0;
  ReactDOM.render(<Parent />, document.getElementById("root"));
}
render();
  • 实现
function useImperativeHandle(ref, factory) {
  ref.current = factory();
}