Pull to refresh
0
0
Николай @bini1988

User

Send message
const cache = new Map< string, string >()
const cacheCopy = {...cache};
const item = cacheCopy.get( 'foo' ) ?? 'bar'

Ваш код напомнил замечательный пример из какого-то доклада где TS позволяет таки выстрелить себе в ногу - валидный с точки зрения типом код сломает runtime.

Предлагаю выносить загромождающие код обработчики в объект класса вместе с зависимостями. Разве так не лучше?

Довольно странно видеть, как автор попробовав путь функциональных хуков вернулся, пусть и не явно, к использованию привычного набора методов объеденных в класс, получив все те недостатки, что были в классовых React компонентах и проигнорировав все те преимущества, что несут функциональные React хуки.

Предлагаю свой вариант приведенного выше синтетического примера в попытке продемонстрировать идею функциональных хуков. Первое, что нам понадобиться это вынести общий код в небольшие функции хелперы, достаточно простые и переиспользуемые:

1. Хук позволяющий вызвать коллбэк с заданным интервалом:

function useInterval<T extends () => void>(cb: T, timeout = 1000) {
  useEffect(() => {
    const intervalHandle = setInterval(cb, timeout);
    return () => clearInterval(intervalHandle);
  }, [cb, timeout]);
}

2. Хук позволяющий хранить некое значение на изменение которого можно подписаться:

function useValue<V>(
  initValue: V,
  onValueChange?: (nextValue: V) => void
): [V, (nextValue: V | ((prevValue: V) => V)) => void] {
  const [value, setValue] = useState(initValue);

  useEffect(() => {
    if (onValueChange) {
      onValueChange(value);
    }
  }, [value, onValueChange]);

  return [value, setValue];
}

3. Хук создающий обработчик для input элементов:

function useInputHandler(handler: (value: string) => void) {
  return useCallback(
    (e: ChangeEvent<HTMLInputElement>) => {
      handler(e.target.value);
    },
    [handler]
  );
}

4. Хук создающий обработчик изменяющий значение на заданную величину:

function useNumHandler(
  diff: number,
  setNumValue: (value: (prevValue: number) => number) => void
) {
  return useCallback(() => {
    setNumValue((value) => value + diff);
  }, [diff, setNumValue]);
}

Еще раз обращаю внимание что у нас появилось 4 небольших хука с вполне понятной и законченной логикой, которые можно переиспользовать в любых компонентах и протестировать независимо друг от друга. Например, мы можем их использовать для рефакторинга приведенного в статье кода:

interface IProps {
  numChanged?: (sum: number) => void;
  stringChanged?: (concatRezult: string) => void;
}

export function SomeComponent(props: IProps) {
  const { numChanged, stringChanged } = props;
  const [numValue, setNumValue] = useValue<number>(0, numChanged);
  const [strValue, setStrValue] = useValue<string>("", stringChanged);
  const onTextChanged = useInputHandler(setStrValue);
  const incNumByOne = useNumHandler(1, setNumValue);
  const decNumByTen = useNumHandler(-10, setNumValue);

  useInterval(incNumByOne);

  return (
    <div>
      <span>{numValue}</span>
      <input type="text" onChange={onTextChanged} value={strValue} />
      <button type="button" onClick={decNumByTen}>
        -10
      </button>
    </div>
  );
}


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

А что делать если достался проблемный легаси, который либо сложно либо невозможно покрыть тестами, поскольку писался он без соблюдения модульности, изолируемости и тестируемости компонентов системы?

Код ниже вполне ок работает без ошибок (в браузере по крайней мере):

const a = { b: 1 }
a.b = 2


Все верно, однако присваивание a = {} выдаст ошибку, что вполне очевидно, на мой взгляд. С объектами const гарантирует, что ссылка будет всегда указывать на заданный при объявении объект. Для объектов с неизменяемыми полями следует смотреть в сторору Object.freeze().

Information

Rating
Does not participate
Location
Санкт-Петербург, Санкт-Петербург и область, Россия
Date of birth
Registered
Activity