状态更新函数是什么
在React中,状态更新函数是用于更新组件状态的函数,它通常与useState
钩子一起使用。useState
是React的一个钩子(Hook),允许你在函数组件中添加状态。当你调用useState
时,它返回一个数组,该数组包含两个元素:当前的状态值和一个更新该状态的函数。这个更新函数就是所谓的状态更新函数。
在React组件开发过程中,将状态更新函数作为React组件属性传递给子组件,是一种常见且有效的状态管理策略,尤其在需要将状态的控制权下放到子组件中时。这种做法不仅有助于保持组件的独立性和可重用性,还能够实现跨组件的状态共享和更新。
在开发过程中发现有些同学将状态更新函数作为React组件属性传递给子组件的做法有点不优雅。下面来介绍一下,并逐步优化它。
tsx
import { useState } from "react";
import type { FC } from 'react';
interface AddProps {
count: number;
onChange: (e: any) => void;
}
const Add: FC<AddProps> = ({ count, onChange }) => {
return (
<button
onClick={() => {
const newCount = count + 1;
onChange(newCount);
}}>
Click me!
</button>
)
}
const MyComponent = () => {
const [count, setCount] = useState(1);
return (
<>
数量:{count}
<Add onChange={setCount} />
</>
);
};
export default MyComponent;
count
是一个多余的组件属性
状态更新函数可以接受一个函数,函数的参数就是上一个状态的值,所以没有必要将状态count
作为组件属性传递给子组件。
diff
import { useState } from "react";
import type { FC } from 'react';
interface AddProps {
- count: number;
onChange: (e: any) => void;
}
const Add: FC<AddProps> = ({ onChange }) => {
return (
<button
onClick={() => {
- const newCount = count + 1;
- onChange(newCount);
+ onChange((data: number) => data + 1);
}}
>
Click me!
</button>
)
}
const MyComponent = () => {
const [count, setCount] = useState(1);
return (
<>
数量:{count}
<Add onChange={setCount} />
</>
);
};
export default MyComponent;
any
类型定义不安全
在上述代码中,为 onChange
属性定义了一个类型为 (e: any) => void
的接口,使用了 TypeScript 中的 any
类型。这种做法虽然简单,但它破坏了类型安全,使得我们的应用更容易出错。得优化,所以在 MyComponent
组件中,我们将 setCount
函数(一个由 useState
钩子返回的状态更新函数)传递给了 Add
组件的 onChange
属性。setCount
可以接受一个数字或一个函数作为参数。因此,我们尝试将 e
的类型定义为 number | ((prevState: number) => number)
。
diff
import { useState } from "react";
import type { FC } from 'react';
interface AddProps {
- onChange: (e: any) => void;
+ onChange: (e: number | ((prevState: number) => number)) => void;
}
const Add: FC<AddProps> = ({ onChange }) => {
return (
<button
onClick={() => {
onChange((data: number) => data + 1);
}}
>
Click me!
</button>
)
}
const MyComponent = () => {
const [count, setCount] = useState(1);
return (
<>
数量:{count}
<Add onChange={setCount} />
</>
);
};
export default MyComponent;
更优雅的类型安全解决方案
TypeScript 和 React 提供了一个更为优雅的类型定义方法:Dispatch<SetStateAction<number>>
。这个类型完美地描述了 setCount
函数,其中 SetStateAction
泛型接受状态的类型(在这个例子中是 number
)作为参数。这不仅提高了代码的类型安全性,还提升了代码的表达力。
diff
import { useState } from "react";
- import type { FC } from 'react';
+ import type { FC, Dispatch, SetStateAction } from 'react';
interface AddProps {
- onChange: (e: number | ((prevState: number) => number)) => void;
+ onChange: Dispatch<SetStateAction<number>>;
}
const Add: FC<AddProps> = ({ onChange }) => {
return (
<button
onClick={() => {
onChange((data: number) => data + 1);
}}
>
Click me!
</button>
)
}
const MyComponent = () => {
const [count, setCount] = useState(1);
return (
<>
数量:{count}
<Add onChange={setCount} />
</>
);
};
export default MyComponent;
借助TypeScript的类型推断能力
通过采用 Dispatch<SetStateAction<number>>
,我们在 Add
组件中直接传递了一个更新函数 (data => data + 1)
给 onChange
属性,无需显式定义 data
的类型,因为 TypeScript 能够从 onChange
的类型定义中自动推断出来。
diff
import { useState } from "react";
import type { FC, Dispatch, SetStateAction } from 'react';
interface AddProps {
onChange: Dispatch<SetStateAction<number>>;
}
const Add: FC<AddProps> = ({ onChange }) => {
return (
<button
onClick={() => {
- onChange((data: number) => data + 1);
+ onChange(data => data + 1);
}}
>
Click me!
</button>
)
}
const MyComponent = () => {
const [count, setCount] = useState(1);
return (
<>
数量:{count}
<Add onChange={setCount} />
</>
);
};
export default MyComponent;