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
。
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
重复问题。
_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
那其他组件用什么?
_state
和index
放在全局作用域重名了咋整
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
交流