【源码】hooks源码简化实现

hooks源码简化实现

相关文章

环境

  • react:18.2.0

hooks源码简化实现


useState

用于在函数组件中添加状态管理。useState 返回一个状态值以及更新该状态值的函数,允许函数组件在不使用类组件的情况下管理组件的状态。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import React, { useState } from 'react';

function Counter() {
// 使用 useState 定义一个名为 count 的状态变量和一个更新该状态的函数 setCount
const [count, setCount] = useState(0);

// 定义一个函数,用于增加 count 的值
const increment = () => {
setCount(count + 1);
};

// 定义一个函数,用于减少 count 的值
const decrement = () => {
setCount(count - 1);
};

return (
<div>
<h1>Counter</h1>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}

export default Counter;

简易源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function useState(initialState) {
// 获取当前组件实例
const component = getCurrentComponent();

// 判断是否已经有状态存在,如果不存在则初始化状态
if (!component.states[component.stateIndex]) {
// 定义一个名为 state 的局部变量,并赋值为 initialState
let state = initialState;

// 定义一个名为 setState 的更新状态的函数
function setState(newState) {
// 将组件的状态数组中对应位置的值更新为新的状态值
component.states[component.stateIndex] = newState;
// 触发组件的重新渲染
// 这里假设有一个名为 scheduleUpdate 的函数用于触发重新渲染
scheduleUpdate();
}

// 将状态值和更新函数返回给调用方
return [state, setState];
} else {
// 如果状态已经存在,则直接返回当前状态值和对应的更新函数
return [
component.states[component.stateIndex],
component.setStateCallbacks[component.stateIndex]
];
}
}

useEffect

用于在函数组件中执行副作用操作,比如数据获取、订阅、DOM 操作等。useEffect 接收一个回调函数和一个依赖数组,当依赖数组中的值发生变化时,会触发回调函数的执行。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import React, { useState, useEffect } from 'react';

function Example() {
// 定义一个状态变量和更新函数,初始值为 0
const [count, setCount] = useState(0);

// 定义一个 effect,每次组件渲染后打印当前 count 值
useEffect(() => {
console.log(`Count: ${count}`);
});

return (
<div>
<p>You clicked {count} times</p>
{/* 点击按钮时,调用 setCount 更新 count 的值 */}
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}

export default Example;

简易源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
function useEffect(callback, dependencies) {
// 获取当前组件
const component = getCurrentComponent();

// 获取上一次的 effect 对象
const lastEffect = component.effects[component.effectIndex];

// 判断依赖项是否发生变化
const dependenciesChanged = !lastEffect ||
!arraysEqual(lastEffect.dependencies, dependencies);

if (dependenciesChanged) {
// 如果依赖项发生变化,则创建新的 effect 对象
const effect = {
callback,
dependencies
};

// 将 effect 对象添加到组件的 effects 数组中
component.effects.push(effect);
}

// 将 effectIndex 指向下一个位置
component.effectIndex++;
}

function getCurrentComponent() {
// 获取当前组件
// 这里假设有一个名为 getCurrentComponent 的函数用于获取当前组件
return getCurrentComponent();
}

function arraysEqual(a, b) {
// 比较两个数组是否相等
// 这里省略具体实现
return arraysEqual(a, b);
}

useContext

用于在函数组件中获取上下文(Context)的值。useContext 接收一个上下文对象作为参数,并返回上下文的当前值。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import React, { createContext, useContext } from 'react';

// 创建一个上下文对象
const ThemeContext = createContext('light');

function App() {
// 使用 useContext 获取上下文的值
const theme = useContext(ThemeContext);

return (
<div className={theme}>
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
}

// 使用上下文对象提供的 Provider 组件在组件树中提供上下文的值
function AppWrapper() {
return (
<ThemeContext.Provider value="dark">
<App />
</ThemeContext.Provider>
);
}

export default AppWrapper;

简易源码

1
2
3
4
5
6
7
function useContext(context) {
// 从当前组件实例中获取上下文
const component = getCurrentComponent();
// 通过上下文对象的 _contextID 属性获取上下文的值
const value = component.context[context._contextID];
return value;
}

useReducer

用于在函数组件中管理复杂的状态逻辑。useReducer 类似于 Redux 中的 reducer,接收一个 reducer 函数和初始状态作为参数,并返回一个状态值和 dispatch 函数,通过 dispatch 函数可以触发状态的更新。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import React, { useReducer } from 'react';

// 定义 reducer 函数
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}

function Counter() {
// 使用 useReducer 定义状态值和 dispatch 函数
const [state, dispatch] = useReducer(reducer, { count: 0 });

return (
<div>
<p>Count: {state.count}</p>
{/* 点击按钮时,触发 dispatch 函数执行对应的操作 */}
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
</div>
);
}

export default Counter;

简易源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function useReducer(reducer, initialState) {
// 获取当前组件实例
const component = getCurrentComponent();

// 初始化状态值为初始状态
let state = initialState;

// 定义 dispatch 函数,用于触发状态的更新
function dispatch(action) {
// 调用 reducer 函数获取新的状态值
state = reducer(state, action);
// 触发组件的重新渲染
// 这里假设有一个名为 scheduleUpdate 的函数用于触发重新渲染
scheduleUpdate();
}

// 返回状态值和 dispatch 函数
return [state, dispatch];
}

useCallback

用于返回一个记忆化的回调函数,避免在每次渲染时创建新的回调函数。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import React, { useState, useCallback } from 'react';

function App() {
// 定义一个状态值用于保存点击次数
const [count, setCount] = useState(0);

// 使用 useCallback 缓存回调函数
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]); // 仅在 count 发生变化时重新创建函数

return (
<div>
<p>Count: {count}</p>
{/* 每次点击按钮都会调用缓存的回调函数 */}
<button onClick={handleClick}>Increment</button>
</div>
);
}

export default App;

简易源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function useCallback(callback, dependencies) {
// 获取当前组件实例
const component = getCurrentComponent();

// 判断依赖项是否发生变化
const dependenciesChanged = !component.lastDependencies ||
!arraysEqual(component.lastDependencies, dependencies);

// 如果依赖项发生变化,则重新创建函数
if (dependenciesChanged) {
component.lastCallback = callback;
component.lastDependencies = dependencies;
}

// 返回缓存的回调函数
return component.lastCallback;
}

useMemo

用于返回一个记忆化的值,仅在依赖项发生变化时重新计算。可以用于优化性能,避免不必要的计算。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import React, { useState, useMemo } from 'react';

function App() {
// 定义一个状态值用于保存输入的数字
const [number, setNumber] = useState(0);

// 使用 useMemo 缓存计算结果
const squaredNumber = useMemo(() => {
console.log('Calculating squared number...');
return number * number;
}, [number]); // 仅在 number 发生变化时重新计算

return (
<div>
<input
type="number"
value={number}
onChange={(e) => setNumber(parseInt(e.target.value))}
/>
<p>Squared number: {squaredNumber}</p>
</div>
);
}

export default App;

简易源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function useMemo(factory, dependencies) {
// 获取当前组件实例
const component = getCurrentComponent();

// 判断依赖项是否发生变化
const dependenciesChanged = !component.lastDependencies ||
!arraysEqual(component.lastDependencies, dependencies);

// 如果依赖项发生变化,则重新计算并更新 lastDependencies
if (dependenciesChanged) {
component.memoizedValue = factory();
component.lastDependencies = dependencies;
}

return component.memoizedValue;
}

useRef

用于在函数组件中创建可变的引用,可以用来存储组件的引用、DOM 元素的引用等。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React, { useRef } from 'react';

function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// 使用 ref 对象中的 .current 属性获取 DOM 节点
inputEl.current.focus();
};

return (
<div>
{/* 将 ref 对象赋值给组件的 DOM 节点 */}
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</div>
);
}

简易源码

1
2
3
4
5
function useRef(initialValue) {
// 创建一个 ref 对象,包含一个名为 .current 的属性
const ref = { current: initialValue };
return ref;
}

useImperativeHandle

用于在函数式组件中自定义向外暴露的实例值或方法。它允许你在函数式组件内部操作 ref 对象,并选择性地向外暴露指定的值或方法。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import React, { useRef, useImperativeHandle } from 'react';

const MyComponent = React.forwardRef((props, ref) => {
const inputRef = useRef(null);

// 使用 useImperativeHandle 定义向外暴露的方法
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
reset: () => {
inputRef.current.value = '';
}
}));

return (
<div>
<input ref={inputRef} type="text" />
</div>
);
});

const ParentComponent = () => {
const childRef = useRef();

const handleFocus = () => {
childRef.current.focus();
};

const handleReset = () => {
childRef.current.reset();
};

return (
<div>
<MyComponent ref={childRef} />
<button onClick={handleFocus}>Focus Input</button>
<button onClick={handleReset}>Reset Input</button>
</div>
);
};

简易源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { useEffect, useRef } from 'react';

function useImperativeHandle(ref, createHandle) {
const handleRef = useRef(null);

useEffect(() => {
if (typeof createHandle === 'function') {
const newHandle = createHandle();
// 将新的 handle 对象中的属性和方法复制到 handleRef.current 中
for (const key in newHandle) {
handleRef.current[key] = newHandle[key];
}
}
}, [createHandle]);

// 将 handleRef.current 赋值给 ref
useEffect(() => {
if (typeof ref === 'function') {
ref(handleRef.current);
} else if (ref !== null && typeof ref === 'object') {
ref.current = handleRef.current;
}
}, [ref]);

return handleRef;
}

export default useImperativeHandle;

useDebugValue

useDebugValue 是 React 提供的一个自定义 Hook,用于在开发者工具中显示自定义的调试信息。它允许开发者在开发过程中更方便地调试自定义 Hook 或其他自定义逻辑。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import React, { useState, useEffect, useDebugValue } from 'react';

// 自定义 Hook,用于获取当前时间
function useCurrentTime() {
const [currentTime, setCurrentTime] = useState(new Date());

// 更新当前时间
useEffect(() => {
const interval = setInterval(() => {
setCurrentTime(new Date());
}, 1000);

return () => clearInterval(interval);
}, []);

// 使用 useDebugValue 在开发者工具中显示当前时间
useDebugValue(currentTime, date => date.toTimeString());

return currentTime;
}

function App() {
const currentTime = useCurrentTime();

return (
<div>
<p>Current Time: {currentTime.toLocaleTimeString()}</p>
</div>
);
}

export default App;

简易源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { useEffect } from 'react';

function useDebugValue(value, formatter) {
useEffect(() => {
if (typeof formatter === 'function') {
// 使用开发者工具中的 React DevTools 显示调试信息
// 这里使用 console.log 来模拟显示调试信息的操作
console.log(formatter(value));
} else {
console.log(value);
}
}, [value, formatter]);
}

export default useDebugValue

喜欢这篇文章?打赏一下支持一下作者吧!
【源码】hooks源码简化实现
https://www.cccccl.com/20240113/源码/react/hooks源码简化实现/
作者
Jeffrey
发布于
2024年1月13日
许可协议