React Hooks浅析

代码分析

const React = window.React;
const ReactDOM = window.ReactDOM;

const App = props=>{
    const [n,setN] = React.useState(0);
    const onClick = ()=>{
        setN(n+1);
    }
    return <div>
        {n}
        <button onClick={onClick}>+1</button>
    </div>
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />,rootElement);

在这里插入图片描述

  • setN
    • setN一定会修改数据,将n+1存入x
    • setN一定会触发重新渲染
  • useState
    • useState肯定会从x读取n的最新值
  • x
    • 每个组件有自己的数据x,命名为state

实现React.useState

const React = window.React;
const ReactDOM = window.ReactDOM;
const rootElement = document.getElementById("root");
let _state
const MyUserState = (initialValue)=>{
    _state = _state === undefined ? initialValue : _state;
    const setState = (newValue)=>{
        _state = newValue;
        render();
    }
    return [_state,setState];
}

const render =()=>{
    return ReactDOM.render(<App/>,rootElement);
}

const App = props=>{
    const [n,setN] =MyUserState(0);
    const onClick = ()=>{
        setN(n+1);
    }
    return <div>
        {n}
        <button onClick={onClick}>+1</button>
    </div>
}
ReactDOM.render(<App />,rootElement);

多个useState的用法

  • 把_state设置为一个对象
    1. 如_state={n:0,m:0}
    2. 不可行,useState(0)不知道变量叫n还是m
  • 把_state设置为数组
    1. 如_state=[0,0]
    2. 可行
  • 代码示例
const React = window.React;
const ReactDOM = window.ReactDOM;
const rootElement = document.getElementById("root");

let _state = [];
let index = 0;
function myUseState(initialValue) {
  const currentIndex = index;
  index += 1;
  _state[currentIndex] = _state[currentIndex] || initialValue;
  function setState(newState) {
    _state[currentIndex] = newState;
    render();
  }
  return [_state[currentIndex], setState];
}
const render = () => {
    index = 0;
    ReactDOM.render(<App />, rootElement);
}
function App() {
  const [n, setN] = myUseState(0);
  const [m, setM] = myUseState(0);
  return (
    <div className="App">
      <p>{n}</p>
      <p>
        <button onClick={() => setN(n + 1)}>+1</button>
      </p>
      <p>{m}</p>
      <p>
        <button onClick={() => setM(m + 1)}>+1</button>
      </p>
    </div>
  );
}
ReactDOM.render(<App />, rootElement);
  • 缺点
    1. useState调用顺序
      1.1 第一次调用顺序:n,m,k
      1.2 那么第二次也必须保证顺序完全一致
      1.3 React不允许出现如下代码
const [n, setN] = React.useState(0);
  let m,setM;
  if(n % 2=== 1){
    [m, setM] =React.useState(0);
  }

图示

在这里插入图片描述

总结

  • 每个函数组件对应一个React节点
  • 每个节点保存着state和index
  • useState会读取state[index]
  • index由useState出现的顺序决定
  • setSate会修改state,并触发更新

useRef

useRef不仅可以用于div,还能用于任意数据,但如果不强制更新的话,它是不会实时更新的

import React from "react";
import ReactDOM from "react-dom";
const rootElement = document.getElementById("root");

function App() {
  const nRef = React.useRef(0);
  const log = () => setTimeout(() => console.log(`n: ${nRef.current}`), 1000);
  return (
    <div className="App">
      <p>{nRef.current} 这里并不能实时更新</p>
      <p>
        <button onClick={() => (nRef.current += 1)}>+1</button>
        <button onClick={log}>log</button>
      </p>
    </div>
  );
}
ReactDOM.render(<App />, rootElement);
  • 强制更新代码
const React = window.React;
const ReactDOM = window.ReactDOM;
const rootElement = document.getElementById("root");

function App() {
  const nRef = React.useRef(0);
  const update = React.useState()[1];
  const log = () => setTimeout(() => console.log(`n: ${nRef.current}`), 1000);
  return (
    <div className="App">
      <p>{nRef.current}</p>
      <p>
        <button onClick={() => ((nRef.current += 1), update(nRef.current))}>
          +1
        </button>
        <button onClick={log}>log</button>
      </p>
    </div>
  );
}

ReactDOM.render(<App />, rootElement);

useContext

  • useContext不仅能惯穿始终,还能惯穿不同组件
  • 代码示例
const React = window.React;
const ReactDOM = window.ReactDOM;
const rootElement = document.getElementById("root");
import "./style.css";

const themeContext = React.createContext(null);

function App() {
    const [theme,setTheme] = React.useState('red');
    return (
        <themeContext.Provider value={{ theme, setTheme }}>
        <div className={`App ${theme}`}>
          <p>{theme}</p>
          <div>
            <ChildA />
          </div>
          <div>
            <ChildB />
          </div>
        </div>
      </themeContext.Provider>
    )
}

function ChildA() {
    const { setTheme } = React.useContext(themeContext);
    return (
      <div>
        <button onClick={() => setTheme("red")}>red</button>
      </div>
    );
  }
  
  function ChildB() {
    const { setTheme } = React.useContext(themeContext);
    return (
      <div>
        <button onClick={() => setTheme("blue")}>blue</button>
      </div>
    );
  }
  
  ReactDOM.render(<App />, rootElement);

//css
.App {
    font-family: sans-serif;
    text-align: center;
  }
  .red button {
    background: red;
    color: white;
  }
  .blue button {
    background: blue;
    color: white;
  }

总结

  1. 每次重新渲染,组件函数就会执行
  2. 对应的所有state都会出现【分身】
  3. 如果不希望出现【分身】,可用:useRef/useContext等
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaobangsky

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值