Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3分钟掌握react hook 在typescript中的姿势 #12

Open
flytam opened this issue Dec 9, 2019 · 0 comments
Open

3分钟掌握react hook 在typescript中的姿势 #12

flytam opened this issue Dec 9, 2019 · 0 comments
Labels
react New feature or request

Comments

@flytam
Copy link
Owner

flytam commented Dec 9, 2019

hook结合typescript可以说是很香了。本文主要介绍hook结合typescript 如何使用,享受ts带给我们的编辑器提示和类型约束

useState

useState如果初始值不是null/undefined的话,是具备类型推导能力的,根据传入的初始值推断出类型;初始值是 null/undefined的话则需要传递类型定义才能进行约束。一般情况下,还是推荐传入类型(通过useState的第一个泛型参数)。

// 这里ts可以推断 value的类型并且能对setValue函数调用进行约束
const [value, setValue] = useState(0);

interface MyObject {
  foo: string;
  bar?: number;
}

// 这里需要传递MyObject才能约束 value, setValue
// 一般情况下推荐传入类型
const [value, setValue] = useState<MyObject>(null);

useContext

useContext一般根据传入的Context的值就可以推断出返回值。不需要显示传递类型

type Theme = 'light' | 'dark';
// 我们在createContext就传了类型了
const ThemeContext = createContext<Theme>('dark');

const App = () => (
  <ThemeContext.Provider value="dark">
    <MyComponent />
  </ThemeContext.Provider>
)

const MyComponent = () => {
    // useContext根据ThemeContext推断出类型,这里不需要显示传
  const theme = useContext(ThemeContext);
  return <div>The theme is {theme}</div>;

useEffect useLayoutEffect

没有返回值,无需传递类型

useCallback useMemo

useMemo无需传递类型,根据函数的返回值就能推断出类型

useCallback无需传递类型,根据函数的返回值就能推断出类型。但是注意函数的入参需要定义类型,不然就是推断为any了!

const value = 10;
// 推断出result是number类型
const result = useMemo(() => value * 2, [value]);

const multiplier = 2;
// 推断出 (value: number) => number
// 注意函数入参value需要定义类型
const multiply = useCallback((value: number) => value * multiplier, [multiplier]);

useRef

useRef传非空初始值的时候可以推断类型,同样也可以通过传入第一个泛型参数来定义类型,约束ref.current的类型。

const MyInput = () => {
  // 这里约束inputRef是一个html元素
  const inputRef = useRef<HTMLInputElement>(null);
  return <input ref={inputRef} />
}
// 自动推断出 myNumberRef.current 是number类型
const myNumberRef = useRef(0);
myNumberRef.current += 1;

useReducer

只需要对传入useReducer的reducer函数的入参stateaction进行类型约束就能够推断出来

interface State {
  value: number;
}

type Action =
  | { type: 'increment' }
  | { type: 'decrement' }
  | { type: 'incrementAmount'; amount: number };

const counterReducer = (state: State, action: Action) => {
  switch (action.type) {
    case 'increment':
      return { value: state.value + 1 };
    case 'decrement':
      return { value: state.value - 1 };
    case 'incrementAmount':
      return { value: state.value + action.amount };
    default:
      throw new Error();
  }
};

// 这里可以推断出state为State类型
const [state, dispatch] = useReducer(counterReducer, { value: 0 });

//能够约束传入dispatch的参数,符合Action类型
dispatch({ type: 'increment' });
dispatch({ type: 'decrement' });
dispatch({ type: 'incrementAmount', amount: 10 });

// TypeScript compilation error
dispatch({ type: 'invalidActionType' });

useImperativeHandle

useImperativeHandle一般比较少用,一般用来选择函数组件对外暴露ref属性被调用,需要配合forwardRef使用。

如下例子。需要定义对外暴露的接口MyInputHandles,函数组件使用React.RefForwardingComponent对外暴露的接口调用作为泛型参数。然后就会得到约束了

// MyInputHandles 需要给父组件的useRef作为类型使用 和 RefForwardingComponent作为泛型参数传入约束
export interface MyInputHandles {
  focus(): void;
}

// 使用RefForwardingComponent 类型进行定义组件,第一个泛型参数是对外暴露的handle,第二个是Props
const MyInput: RefForwardingComponent<MyInputHandles, MyInputProps> = (
  props,
  ref
) => {
  const inputRef = useRef<HTMLInputElement>(null);

  useImperativeHandle(ref, () => ({
    // 这里的返回会自动使用MyInputHandles进行类型约束
    focus: () => {
      if (inputRef.current) {
        inputRef.current.focus();
      }
    },
  }));

  return <input {...props} ref={inputRef} />;
};

// 函数组件必须使用forwardRef才能让外部组件使用该组件的ref
export default forwardRef(MyInput);
// 父组件
const Autofocus = () => {
  // 能够约束 myInputRef.current的类型
  const myInputRef = useRef<MyInputHandles>(null);

  useEffect(() => {
    if (myInputRef.current) {
      myInputRef.current.focus();
    }
  });

  return <MyInput ref={myInputRef} />
}

参考:

React Hooks in TypeScript

@flytam flytam added the Go label Dec 9, 2019
@flytam flytam added react New feature or request and removed Go labels Jun 9, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
react New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant