React Hook原理初探

本篇文章旨在用 最简单的代码 解释React Hook的 基础原理

  1. UseState
const hooks = [];
let index = 0;
const useState = (initial) => {
  const value = hooks[index] || initial;
  const setValue = ((i) => (newVal) => {
    if (hooks[i] === newVal) {
      return
    }
    
    hooks[i] = newVal;
    render();
  })(index);
  index += 1;
  
  return [ value, setValue ];
}

function App() {
  const [name, setName] = useState("apple");
   
  //if (name === "tangerine") {
    //const [other, setOther] = useState(10);
    //console.log(`other ---> ${other}`)
 //}

  const [age, setAge] = useState(20);
  
  setTimeout(() => {
    setName("tangerine");
    setAge(24);
  }, 2000)

  return `My Name Is ${name}, My Age Is ${age}`;
}

function render() {
  // 重置index
  index = 0;
  document.body.innerHTML = App();
}

render();

执行以上代码,视图发生了变化My Name Is apple, My Age Is 20 ---> My Name Is tangerine, My Age Is 24
原理解析:利用数组存储了所有状态变量,index代表的是变量在数组中的顺序。当调用setValue方法改变状态值时,会重新执行整个组件函数,并在之前会重置index0,原因在于重新执行组件函数时会再次依照状态变量的申明顺序去hooks中去获取值。
基于以上,便可理解为何不能在条件语句中申明状态变量,如:

if (***) {
  const [ sex ] = useState("male")
}

条件的变动会影响状态变量的顺序读值,即hooks中的index出现混乱。可将上述代码中注释部分放开,测试结果。

  1. UseEffect
const hooks = [];
const cbs = [];
let index = 0;
const useState = (initial) => {
  const value = hooks[index] || initial;
  const setValue = ((i) => (newVal) => {
    if (hooks[i] === newVal) {
      return
    }
    
    hooks[i] = newVal;
    render();
  })(index);
  index += 1;
  
  return [ value, setValue ];
}
const useEffect = (cb, deps) => {
  if (!deps) {
    cbs.push(cb);

    return;
  }

  const lastDeps = hooks[index];
  const isChanged = !lastDeps ? true : !lastDeps.every((item, index) => item === deps[index]);
  if (isChanged) { 
    cbs.push(cb);
    hooks[index] = deps;
  }
  index += 1;
}

function App() {
  const [name, setName] = useState("apple");
  const [age, setAge] = useState(20);
  
  useEffect(() => {
    console.log("no deps", document.body.innerHTML);
  });

  useEffect(() => {
    console.log(`name is ${name}`)
  }, [name]);

  useEffect(() => {
    console.log(`age is ${age}`)
  }, [age])

  setTimeout(() => {
     setName("tangerine")
  }, 2000);

  return `My Name Is ${name}, My Age Is ${age}`;
}

function render() {
  // 重置index
  index = 0;
  document.body.innerHTML = App();

  while(cbs.length) {
    cbs.splice(0, 1)[0]();
  }
}

render();

执行以上代码,视图发生了变化My Name Is apple, My Age Is 20 --> My Name Is tangerine, My Age Is 20。控制台依次打印出no deps My Name Is apple, My Age Is 20name is appleage is 20no deps My Name Is tangerine, My Age Is 20name is tangerine,可以发现useEffect包裹的函数会在render渲染完成之后执行一遍,对应生命周期componentDidMount。当状态变量name发生变化时,无依赖函数和依赖name函数会执行。
原理解析:利用数组存储了useEffect函数依赖项,执行useEffect函数时,如果没有依赖项,将其参数函数放进队列;如果有依赖项,会找到其依赖项上次状态与此次状态加以比较,如果发生了变化,将其参数函数放进队列。在渲染完成后执行队列任务并 清空

你可能感兴趣的:(React Hook原理初探)