|
189 | 189 | </html> |
190 | 190 | ``` |
191 | 191 |
|
192 | | -上面这个实现,已经可以满足我的需求了,那为啥 React 用的不是数组 + 下标,而是链表?这个到底有啥好处 |
| 192 | +上面这个实现,已经可以满足我的需求了,那为啥 React 用的不是数组 + 下标,而是链表?这个到底有啥好处。 |
| 193 | +先不着急,我发现了一个问题。我上面这个写法,每次setState的时候就会render一次。我先来解决这个问题。 |
| 194 | +``` html |
| 195 | +<!DOCTYPE html> |
| 196 | +<html lang="en"> |
| 197 | +<head> |
| 198 | + <meta charset="UTF-8" /> |
| 199 | + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| 200 | + <title>Mini useState</title> |
| 201 | +</head> |
| 202 | +<body> |
| 203 | + <div id="root"></div> |
| 204 | + |
| 205 | + <script> |
| 206 | + let hooks = []; |
| 207 | + let hookIndex = 0; |
| 208 | + let renderCount = 0; |
| 209 | + let isBatching = false; |
| 210 | +
|
| 211 | + function batch(fn) { |
| 212 | + isBatching = true; |
| 213 | + fn(); |
| 214 | + isBatching = false; |
| 215 | + render(); |
| 216 | + } |
| 217 | +
|
| 218 | + function useState(initialState) { |
| 219 | + const currentIndex = hookIndex; |
| 220 | +
|
| 221 | + if (hooks[currentIndex] === undefined) { |
| 222 | + hooks[currentIndex] = { |
| 223 | + memoizedState: |
| 224 | + typeof initialState === 'function' ? initialState() : initialState, |
| 225 | + queue: [], |
| 226 | + }; |
| 227 | + } |
| 228 | +
|
| 229 | + const hook = hooks[currentIndex]; |
| 230 | +
|
| 231 | + if (hook.queue.length > 0) { |
| 232 | + let newState = hook.memoizedState; |
| 233 | +
|
| 234 | + for (const action of hook.queue) { |
| 235 | + newState = |
| 236 | + typeof action === 'function' |
| 237 | + ? action(newState) |
| 238 | + : action; |
| 239 | + } |
| 240 | +
|
| 241 | + hook.memoizedState = newState; |
| 242 | + hook.queue = []; |
| 243 | + } |
| 244 | +
|
| 245 | + function setState(action) { |
| 246 | + hook.queue.push(action); |
| 247 | +
|
| 248 | + if (!isBatching) { |
| 249 | + render(); |
| 250 | + } |
| 251 | + } |
| 252 | +
|
| 253 | + hookIndex++; |
| 254 | +
|
| 255 | + return [hook.memoizedState, setState]; |
| 256 | + } |
| 257 | +
|
| 258 | + function App() { |
| 259 | + const [age, setAge] = useState(18); |
| 260 | + const [name, setName] = useState('Jack'); |
| 261 | + const [address, setAddress] = useState('Github Repo'); |
| 262 | + const [techStack, setTechStack] = useState('JavaScript'); |
| 263 | +
|
| 264 | + function handleAgeClick() { |
| 265 | + setAge((prev) => prev + 1); |
| 266 | + setAge((prev) => prev + 1); |
| 267 | + setAge((prev) => prev + 1); |
| 268 | + } |
| 269 | +
|
| 270 | + function handleNameClick() { |
| 271 | + setName('Tom'); |
| 272 | + } |
| 273 | +
|
| 274 | + function handleAddressClick() { |
| 275 | + setAddress('Gitlab Repo'); |
| 276 | + } |
| 277 | +
|
| 278 | + function handleTechStackClick() { |
| 279 | + setTechStack((prev) => |
| 280 | + prev === 'JavaScript' ? 'React' : 'JavaScript' |
| 281 | + ); |
| 282 | + } |
| 283 | +
|
| 284 | + window.handleAgeClick = handleAgeClick; |
| 285 | + window.handleNameClick = handleNameClick; |
| 286 | + window.handleAddressClick = handleAddressClick; |
| 287 | + window.handleTechStackClick = handleTechStackClick; |
| 288 | + window.batch = batch; |
| 289 | +
|
| 290 | + console.log('hooks:', hooks); |
| 291 | +
|
| 292 | + return ` |
| 293 | + <div> |
| 294 | + <button onclick="batch(window.handleAgeClick)"> |
| 295 | + age: ${age} |
| 296 | + </button> |
| 297 | +
|
| 298 | + <div onclick="batch(window.handleNameClick)"> |
| 299 | + name: ${name} |
| 300 | + </div> |
| 301 | +
|
| 302 | + <div onclick="batch(window.handleAddressClick)"> |
| 303 | + address: ${address} |
| 304 | + </div> |
| 305 | +
|
| 306 | + <div onclick="batch(window.handleTechStackClick)"> |
| 307 | + techStack: ${techStack} |
| 308 | + </div> |
| 309 | + </div> |
| 310 | + `; |
| 311 | + } |
| 312 | +
|
| 313 | + function render() { |
| 314 | + hookIndex = 0; |
| 315 | + renderCount++; |
| 316 | +
|
| 317 | + console.log('渲染了', renderCount, '次'); |
| 318 | +
|
| 319 | + document.getElementById('root').innerHTML = App(); |
| 320 | + } |
| 321 | +
|
| 322 | + render(); |
| 323 | + </script> |
| 324 | +</body> |
| 325 | +</html> |
| 326 | +``` |
0 commit comments