[React] Custom useState and useEffect hook

import { flushSync } from 'react-dom'
import { createRoot } from 'react-dom/client'

let hookIndex = 0
const states: Array<[any, (newState: any) => void]> = []
const INITIALIZATION = Symbol('INITIALIZATION')
const UPDATE = Symbol('UPDATE')
type Phase = typeof INITIALIZATION | typeof UPDATE
let phase = INITIALIZATION

/**
 * 1. Multiple useState problem
 * a. We need to use `INITIALIZATION` and `UPDATE symbol to keep track of the phase
 * b. We need a Hook id to keep track of the current hook
 * 	- ID will be from 0 and onwards
 *  - ID will be reset to 0 for each render
 */
function useState<T>(initialState: T) {
	const id = hookIndex++
	if (phase === INITIALIZATION) {
		states[id] = [
			initialState,
			(newState: T) => {
				states[id][0] = newState
				render(UPDATE)
			},
		]
	}

	return states[id] as [T, (newState: T) => void]
}

type EffectCallback = () => void
const effects: Array<{
	callback: EffectCallback
	deps?: any[]
	prevDeps?: any[]
}> = []

function useEffect(callback: EffectCallback, deps?: any[]) {
	const id = hookIndex++
	const prevDeps = effects[id]?.deps
	effects[id] = { callback, deps, prevDeps }
}

function Counter() {
	const [count, setCount] = useState(0)
	const [enabled, setEnabled] = useState(true)

	const increment = () => setCount(count + 1)
	const toggle = () => setEnabled(!enabled)

	useEffect(() => {
		console.log('efftivec')
	}, [enabled])

	return (
		<div className="counter">
			<button onClick={increment}>{count}</button>
			<button onClick={toggle}>{enabled ? 'Disable' : 'Enable'}</button>
		</div>
	)
}

const rootEl = document.createElement('div')
document.body.append(rootEl)
const appRoot = createRoot(rootEl)

function render(newPhase: Phase) {
	hookIndex = 0
	phase = newPhase

	flushSync(() => {
		appRoot.render(<Counter />)
	})

	for (const effect of effects) {
		if (!effect) continue

		const hasDepsChanged = effect.deps
			? !effect.deps.every((dep, i) => Object.is(dep, effect.prevDeps?.[i]))
			: true

		if (hasDepsChanged) {
			effect.callback()
		}
	}
}

render(INITIALIZATION)

 

posted @ 2024-07-31 14:37  Zhentiw  阅读(2)  评论(0编辑  收藏  举报