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设置为一个对象
- 如_state={n:0,m:0}
- 不可行,useState(0)不知道变量叫n还是m
- 把_state设置为数组
- 如_state=[0,0]
- 可行
- 代码示例
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);
- 缺点
- useState调用顺序
1.1 第一次调用顺序:n,m,k
1.2 那么第二次也必须保证顺序完全一致
1.3 React不允许出现如下代码
- useState调用顺序
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;
}
总结
- 每次重新渲染,组件函数就会执行
- 对应的所有state都会出现【分身】
- 如果不希望出现【分身】,可用:useRef/useContext等