Как ввести пользовательский хук useStateWithCallback React TypeScript - PullRequest
1 голос
/ 14 июля 2020

У меня проблема с вводом следующего настраиваемого React hook, я новичок в TypeScript, и это вызывает некоторую путаницу. как правильно использовать any? Поскольку это универсальная функция и ее можно использовать где угодно, как мне ее правильно набрать?

Я добавил некоторые из своих попыток прямо в свой пример и, как вы можете видеть, остановился, потому что со своей стороны в итоге останется только any типов.

1 Ответ

1 голос
/ 15 июля 2020

Во-первых, вам нужно заставить этот useStateCallback принять общий c параметр, который представляет ваше состояние. Вы собираетесь часто использовать этот параметр. Мы назовем это S для состояния.

function useStateCallback<S>(initialState: S) { ... }

Далее идет редуктор. Похоже, вам нужно только одно действие, которое принимает Partial из S, которое объединяется с состоянием. Итак, для двух общих параметров c в Reducer мы используем S для состояния и Partial<S> для действия.

const [state, setState] = useReducer<Reducer<S, Partial<S>>>(
  (state, newState) => ({ ...state, ...newState }),
  // state is implicitly typed as: S
  // newState is implicitly typed as: Partial<S>

  initialState
)

Или вы можете ввести аргументы функции редуктора и эти типы будут выведены, что выглядит немного чище, ИМХО.

const [state, setState] = useReducer(
  (state: S, newState: Partial<S>) => ({ ...state, ...newState }),
  initialState
)

Для создания ссылки нам нужно дать ей тип функции обратного вызова, объединенной с null, поскольку она не всегда может содержат значение:

const cbRef = useRef<((state: S) => void) | null>(null)

для setStateCallback, нам нужно принять Partial<S> для слияния с полным состоянием и обратный вызов, который имеет полное состояние в качестве единственного аргумента:

function setStateCallback(state: Partial<S>, cb: (state: S) => void) {
  cbRef.current = cb
  setState(state)
}

Ваш эффект должен быть таким, как есть.

Последнее, что нужно сделать, это изменить ваш возврат на:

return [state, setStateCallback] as const

Это необходимо, потому что машинописный текст видит это как массив по умолчанию, но вы хотите, чтобы это был кортеж. Вместо массива (S | Callback)[] вы хотите, чтобы это был кортеж ровно с двумя элементами типа [S, Callback]. Добавление as const к массиву указывает машинописному тексту рассматривать массив как константу и фиксировать эти типы в правильных позициях.

Собирая все это вместе, вы получаете:

import React, { useReducer, useRef, useEffect, Reducer } from 'react'

function useStateCallback<S>(initialState: S) {
  const [state, setState] = useReducer<Reducer<S, Partial<S>>>(
    (state, newState) => ({ ...state, ...newState }),
    initialState
  )
  const cbRef = useRef<((state: S) => void) | null>(null)

  function setStateCallback(state: Partial<S>, cb: (state: S) => void) {
    cbRef.current = cb
    setState(state)
  }

  useEffect(() => {
    if (cbRef.current) {
      cbRef.current(state)
      cbRef.current = null
    }
  }, [state])

  return [state, setStateCallback] as const
}

// Type safe usage
function Component() {
  const [state, setStateCallback] = useStateCallback({ foo: 'bar' })

  console.log(state.foo)

  setStateCallback({ foo: 'baz' }, newState => {
    console.log(newState.foo)
  })

  return <div>{state.foo}</div>
}

Детская площадка

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...