setState
App
会被调用,获得虚拟dom
,建立真实的dom
button
时,调用setN
,再次调用App
,获得虚拟dom
,用DOM Diff
算法更新dom
import React from "react"; import ReactDOM from "react-dom"; const rootElement = document.getElementById("root"); function App() { const [n, setN] = React.useState(0); console.log("App 运行了"); // App 被调用就会执行一次 console.log(`n: ${n}`); // App 被调用后 n 每次都不同 return ( <div className="App"> <p>{n}</p> <p> <button onClick={() => setN(n + 1)}>+1</button> </p> </div> ); } ReactDOM.render(<App />, rootElement);
n
在App
被调用后每次都会变化,可是setN()
却不会改变n
。react
setN
必定会修改数据x
,将n + 1
存入x
setN
必定会触发App
从新渲染`useState
确定会从x
读取n
的最新值x
,咱们将其命名成state
setState
myUseState
,接收一个初始值initialValue
initialValue
赋值给一个中间变量state
setState
,接收一个newValue
,再将newValue
赋值给state
,并执行App
state
和setState
import React from "react"; import ReactDOM from "react-dom"; const rootElement = document.getElementById("root"); const myUseState = initialValue => { let state = initialValue; const setState = newValue => { state = newValue; render(); }; return [state, setState]; }; const render = () => { ReactDOM.render(<App />, rootElement); }; function App() { const [n, setN] = myUseState(0); return ( <div className="App"> <p>{n}</p> <p> <button onClick={() => setN(n + 1)}>+1</button> </p> </div> ); } ReactDOM.render(<App />, rootElement);
可是这样有个问题每次执行setN
时,都会把state
设置为初始值,由于每次执行setN
都会传入一个初始值0
算法
解决这个问题就是将state
变成全局变量数组
import React from "react"; import ReactDOM from "react-dom"; const rootElement = document.getElementById("root"); let _state; const myUseState = initialValue => { _state = _state === undefined ? initialValue : _state; const setState = newValue => { _state = newValue; render(); }; return [_state, setState]; }; const render = () => { ReactDOM.render(<App />, rootElement); }; function App() { const [n, setN] = myUseState(0); return ( <div className="App"> <p>{n}</p> <p> <button onClick={() => setN(n + 1)}>+1</button> </p> </div> ); } ReactDOM.render(<App />, rootElement);
useState
function App() { const [n, setN] = myUseState(0); const [M, setM] = myUseState(1); // 会出问题,第一个会被覆盖 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> ); }
因为全部数据都存在一个_state
中,因此会冲突。微信
可使用数组去解决_state
重复问题。dom
_state
声明为[]
,同时声明一个索引index = 0
myUseState
方法内部声明一个临时变量currentIndex
,用来保存索引index
_state
setState
时也将经过索引去操做_state
index += 1
_state[currentIndex]
和setState
render
方法是将index
重置为0
import React from "react"; import ReactDOM from "react-dom"; const rootElement = document.getElementById("root"); let _state = []; let index = 0; const myUseState = initialValue => { const currentIndex = index; _state[currentIndex] = _state[currentIndex] === undefined ? initialValue : _state[currentIndex]; const setState = newValue => { _state[currentIndex] = newValue; render(); }; index += 1; 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)}>N+1</button> </p> <p>{m}</p> <p> <button onClick={() => setM(m + 1)}>M+1</button> </p> </div> ); } ReactDOM.render(<App />, rootElement);
useState
调用顺序n
是第一个,m
是第二个,k
是第三个React
不容许出现以下代码function App() { const [n, setN] = myUseState(0); let m, setM; if (n % 2 === 1) [m, setM] = myUseState(0); // 报错 return ( <div className="App"> <p>{n}</p> <p> <button onClick={() => setN(n + 1)}>N+1</button> </p> <p>{m}</p> <p> <button onClick={() => setM(m + 1)}>M+1</button> </p> </div> ); }
报错信息:React has detected a change in the order of Hooks called by App. This will lead to bugs and errors if not fixed.
函数
App
用了_state
和index
那其余组件用什么?post
_state
和index
放在全局做用域重名了咋整code
bug
function App() { const [n, setN] = myUseState(0); const log () => setTimeout(() => console.log(`n: ${n}`), 3000) return ( <div className="App"> <p>{n}</p> <p> <button onClick={() => setN(n + 1)}>N+1</button> <button onClick={log}>log</button> </p> </div> ); }
先点击N+1
时,再点击log
,输出是没有问题对象
若是先点击log
,在点击N+1
,就会发现输出的竟然是0
。难道N+1
后输出不是1
么。索引
由于setN
是不会改变n
,而是生成一个新的n
。
bug
window.xxx
useRef
不只能够用于div
,还能用于任意数据useContext
不能能贯穿始终,还能贯穿不一样组件function App() { const nRef = React.useRef(0); // { current: 0 } const log () => setTimeout(() => console.log(`n: ${React.useRef(0)}`), 3000); const update = React.useState(null)[1]; return ( <div className="App"> <p>{nRef.current}</p> <p> <button onClick={() => { nRef.current += 1; // 这里不能实时更新 update(nRef.current; )}}>N+1</button> <button onClick={log}>log</button> </p> </div> ); }
React.current += 1
不会让App
从新渲染。
React
节点state
和index
useState
会读取state[index]
index
由useState
出现的顺序决定setState
会修改state
,并触发更新。Tips
:
React
作了简化,方便理解。React
对象节点应该是FiberNode
_state
的真实名称为memorizedState
;index
的实现用到了链表 另外可添加微信ttxbg180218
交流