是否存在使用 React hooks 和 Typescript 定义状态的 "correct" 方式?
Is there such a thing as a "correct" way of defining state with React hooks and Typescript?
我使用 React 已经有一段时间了,昨天我在一个基于 Typescript 的项目中接触到了 hooks。在重构之前,class 的状态是这样的:
interface INavItemProps {
route: IRoute;
}
interface INavItemState {
toggleStateOpen: boolean
}
class NavItem extends Component<INavItemProps, INavItemState> {
constructor() {
this.state = { toggleStateOpen: false };
}
public handleClick = (element: React.MouseEvent<HTMLElement>) => {
const toggleState = !this.state.toggleStateOpen;
this.setState({ toggleStateOpen: toggleState });
};
...
}
现在,当重构为功能组件时,我从这个开始
interface INavItemProps {
route: IRoute;
}
const NavItem: React.FunctionComponent<INavItemProps> = props => {
const [toggleState, setToggleState] = useState<boolean>(false);
const { route } = props;
const handleClick = (element: React.MouseEvent<HTMLElement>) => {
const newState = !toggleState;
setToggleState(newState);
};
...
}
但后来我也测试了这个:
interface INavItemProps {
route: IRoute;
}
interface INavItemState {
toggleStateOpen: boolean
}
const NavItem: React.FunctionComponent<INavItemProps> = props => {
const [state, setToggleState] = useState<INavItemState>({toggleStateOpen: false});
const { route } = props;
const handleClick = (element: React.MouseEvent<HTMLElement>) => {
const newState = !state.toggleStateOpen;
setToggleState({toggleStateOpen: newState});
};
...
}
在这种情况下,是否存在定义状态的正确方法?或者我应该简单地为状态的每个切片调用更多挂钩?
useState
hook 允许您定义任何类型的状态,如对象、数组、数字、字符串、布尔值等。您需要知道的是 hooks updater 不会合并其上的状态自己的 unline setState,所以如果你维护一个数组或一个对象并且你只将要更新的值传递给更新程序,它基本上会导致你的其他状态丢失。
通常情况下,最好使用多个挂钩而不是使用具有一个 useState
挂钩的对象,或者如果您愿意,您可以编写自己的自定义挂钩来合并值,例如
const useMergerHook = (init) => {
const [state, setState] = useState(init);
const updater = (newState) => {
if (Array.isArray(init)) {
setState(prv => ([
...prv,
...newState
]))
} else if(typeof init === 'object' && init !== null) {
setState(prv => ({
...prv,
...newState
}))
} else {
setState(newState);
}
}
return [state, updater];
}
或者如果 state/state 更新需要更复杂并且处理程序需要传递给组件,我建议使用 useReducer
挂钩,因为您有多个逻辑来更新状态和可以利用嵌套对象等复杂状态并有选择地编写更新逻辑
我使用 React 已经有一段时间了,昨天我在一个基于 Typescript 的项目中接触到了 hooks。在重构之前,class 的状态是这样的:
interface INavItemProps {
route: IRoute;
}
interface INavItemState {
toggleStateOpen: boolean
}
class NavItem extends Component<INavItemProps, INavItemState> {
constructor() {
this.state = { toggleStateOpen: false };
}
public handleClick = (element: React.MouseEvent<HTMLElement>) => {
const toggleState = !this.state.toggleStateOpen;
this.setState({ toggleStateOpen: toggleState });
};
...
}
现在,当重构为功能组件时,我从这个开始
interface INavItemProps {
route: IRoute;
}
const NavItem: React.FunctionComponent<INavItemProps> = props => {
const [toggleState, setToggleState] = useState<boolean>(false);
const { route } = props;
const handleClick = (element: React.MouseEvent<HTMLElement>) => {
const newState = !toggleState;
setToggleState(newState);
};
...
}
但后来我也测试了这个:
interface INavItemProps {
route: IRoute;
}
interface INavItemState {
toggleStateOpen: boolean
}
const NavItem: React.FunctionComponent<INavItemProps> = props => {
const [state, setToggleState] = useState<INavItemState>({toggleStateOpen: false});
const { route } = props;
const handleClick = (element: React.MouseEvent<HTMLElement>) => {
const newState = !state.toggleStateOpen;
setToggleState({toggleStateOpen: newState});
};
...
}
在这种情况下,是否存在定义状态的正确方法?或者我应该简单地为状态的每个切片调用更多挂钩?
useState
hook 允许您定义任何类型的状态,如对象、数组、数字、字符串、布尔值等。您需要知道的是 hooks updater 不会合并其上的状态自己的 unline setState,所以如果你维护一个数组或一个对象并且你只将要更新的值传递给更新程序,它基本上会导致你的其他状态丢失。
通常情况下,最好使用多个挂钩而不是使用具有一个 useState
挂钩的对象,或者如果您愿意,您可以编写自己的自定义挂钩来合并值,例如
const useMergerHook = (init) => {
const [state, setState] = useState(init);
const updater = (newState) => {
if (Array.isArray(init)) {
setState(prv => ([
...prv,
...newState
]))
} else if(typeof init === 'object' && init !== null) {
setState(prv => ({
...prv,
...newState
}))
} else {
setState(newState);
}
}
return [state, updater];
}
或者如果 state/state 更新需要更复杂并且处理程序需要传递给组件,我建议使用 useReducer
挂钩,因为您有多个逻辑来更新状态和可以利用嵌套对象等复杂状态并有选择地编写更新逻辑