网站搜索

reactjs - 传递给组件的 setState 应该设置什么 Typescript 类型


我有一些父组件,比如:

const ParentOne = () => {
  const [error, setError] = useState<{ one: boolean }>({ one: false });
...omit
  return (
    <>
      <Child setErr={setError} name={"one"} />
    </>
  );
};

const ParentTwo = () => {
  const [error, setError] = useState<{ one: boolean; two: boolean }>({
    one: false,
    two: false,
  });
...omit
  return (
    <>
      <Child setErr={setError} name={"one"} />
      <Child setErr={setError} name={"two"} />
    </>
  );
};

以及具有不同数量的子组件和不同

const[...] =  useState<{ one: boolean; two: boolean; ...n: boolean }>({
        one: false,
        two: false,
        ...,
        n: false    
      });

这是我的子组件:

const Child: FC<IChild> = ({ setErr, name }) => {
  const handleClick = () => {
    setErr((prev) => ({ ...prev, [name]: true }));
  };
  return <button onClick={handleClick}/>;
};

我不知道应该为 setErr 设置哪种类型,因为 useState 对不同的父组件有不同的泛型。我尝试了这样做:

interface IChild {
  setErr: React.Dispatch<React.SetStateAction<{ [key: string]: boolean }>>;
  name: string;
}

但在每个父组件中,setErr 需要 IChild 中类型与 useState 中泛型完全匹配

答案#1

你所写的带有 Child 的 setErr 属性的真实类型是:

type ErrorSetter = React.Dispatch<React.SetStateAction<{[key: string]: boolean}>>;

我看到两个选择:

  1. 按原样使用Child,并对setError使用类型断言(有几种方法,下面会详细说明)。

  2. 使用自定义钩子并简化 Child

我的偏好是#2,但让我们看看它们每一个:

使用 Child 并按类型断言书写

所以如果我们使用那个错误设置器,我们有:

type ErrorSetter = React.Dispatch<React.SetStateAction<{[key: string]: boolean}>>;
interface IChild {
  setErr: ErrorSetter;
  name: string;
}

如何在不经常写as的情况下使用它?

总体来说,我们不会这样做,但你可能只对每个父组件做一次:

const ParentTwo = () => {
  const [error, setError] = useState({
    one: false,
    two: false,
  });
  const setErr = setError as ErrorSetter;
//
  return (
    <>
      <Child setErr={setErr} name="one" />
      <Child setErr={setErr} name="two" />
    </>
  );
};

这相当方便,并且保持了 Child 不变,尽管正如类型断言常常(总是?)的情况一样,它并不是完全类型安全的。

使用自定义钩子

但我倾向于使用自定义钩子并简化 Child。这个钩子是:

const useErrorState = <T,>(initial: T): [T, (name: keyof T) => void] => {
  const [error, realSetError] = useState(initial);
  const setError = useCallback(
    (name: keyof T) => {
      realSetError(prev => ({...prev, [name]: true}));
    },
    []
  );
  return [error, setError];
};

如果你不需要 setErr 保持稳定,那么你可以这样使用它:

const ParentTwo = () => {
  const [error, setError] = useErrorState({
    one: false,
    two: false,
  });
//
  return (
    <>
      <Child setErr={() => setError("one")} />
      <Child setErr={() => setError("two")} />
    </>
  );
};

游乐场链接

...或者如果你这样做是为了避免 Child 重新渲染(这假设 Child 使用了 React.memo 或者 [在类组件中] PureComponent 或 shouldComponentUpdate):

const ParentTwo = () => {
  const [error, setError] = useErrorState({
    one: false,
    two: false,
  });
  const setErrorOne = useCallback(() => setError("one"), []);
  const setErrorTwo = useCallback(() => setError("two"), []);
//
  return (
    <>
      <Child setErr={setErrorOne} />
      <Child setErr={setErrorTwo} />
    </>
  );
};

游乐场链接