React — Закройте зависимость!

В чем разница между пользовательским хуком и функциональным компонентом?


(распространенная) проблема

У вас есть компонент и вам нужно управлять его состоянием, и он отлично работает:

function BaseExample() {
  const [option, setOption] = useState('two');

  const handleChange = (el) => {
    setOption(el.target.value);
  };

  return (
    <div>
      <select
        onChange={handleChange}
        value={option}
      >
        {[
          { value: 'one', label: 'One' },
          { value: 'two', label: 'Two' },
          { value: 'three', label: 'Three' },
        ].map((option) => (
          <option
            key={option.value}
            value={option.value}
          >
            {option.label}
          </option>
        ))}
      </select>
      <div>{option ? `Selected: ${option}` : 'No selection'}</div>
    </div>
  );
}
Вход в полноэкранный режим Выход из полноэкранного режима

Но что происходит, когда вы пытаетесь его рефакторить?

function RefactoredExample() {
  const [option, setOption] = useState('two');

  const handleChange = (el) => {
    setOption(el.target.value);
  };

  return (
    <div>
      {SelectComponent(handleChange, option)}
      <div>{option ? `Selected: ${option}` : 'No selection'}</div>
    </div>
  );
}

function SelectComponent(handleChange, option) {
  return (
    <select
      onChange={handleChange}
      value={option}
    >
      {[
        { value: 'one', label: 'One' },
        { value: 'two', label: 'Two' },
        { value: 'three', label: 'Three' },
      ].map((option) => (
        <option
          key={option.value}
          value={option.value}
        >
          {option.label}
        </option>
      ))}
    </select>
  );
}
Вход в полноэкранный режим Выход из полноэкранного режима

Теперь у нас есть один компонент, который должен знать слишком много, и другой, который ничего не может сделать самостоятельно.

Вводим пользовательские хуки

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

Что угодно? Да, даже JSX.

function RefactoredWithHookExample() {
  const { option, SelectComponent } = useSelectComponent();

  return (
    <div>
      <SelectComponent />
      <div>{option ? `Selected: ${option}` : 'No selection'}</div>
    </div>
  );
}

function useSelectComponent() {
  const [option, setOption] = useState('two');

  const handleChange = (el) => {
    setOption(el.target.value);
  };

  const SelectComponent = () => (
    <select
      onChange={handleChange}
      value={option}
    >
      {[
        { value: 'one', label: 'One' },
        { value: 'two', label: 'Two' },
        { value: 'three', label: 'Three' },
      ].map((option) => (
        <option
          key={option.value}
          value={option.value}
        >
          {option.label}
        </option>
      ))}
    </select>
  );

  return { option, SelectComponent };
}
Войти в полноэкранный режим Выход из полноэкранного режима

Теперь SelectComponent знает все, что ему нужно для управления своим состоянием, а родительский компонент знает только то, что ему нужно.

С замыканиями можно делать все, что угодно!

Такой пример вряд ли можно назвать захватывающим, но помните, что из хука можно вернуть все, что угодно!

Мало того, это может работать как закрытие, так что вы можете получить что-то вроде этого:

function RefactoredWithClosureHookExample() {
  const { option, SelectComponent } = useSelectComponent({
    options: [
      { value: 'one', label: 'One' },
      { value: 'two', label: 'Two' },
      { value: 'three', label: 'Three' },
    ],
    initial: 'two',
  });

  return (
    <div>
      <SelectComponent
        selectProps={{ style: { color: 'red' } }}
        optionProps={{ style: { color: 'green' } }}
      />
      <div>{option ? `Selected: ${option}` : 'No selection'}</div>
    </div>
  );
}

function useSelectComponent({ options, initial }) {
  const [option, setOption] = useState(initial);

  const handleChange = (el) => {
    setOption(el.target.value);
  };

  const SelectComponent = ({ selectProps, optionProps }) => (
    <select
      onChange={handleChange}
      value={option}
      {...selectProps}
    >
      {options.map((option) => (
        <option
          key={option.value}
          value={option.value}
          {...optionProps}
        >
          {option.label}
        </option>
      ))}
    </select>
  );

  return { option, SelectComponent };
}
Войти в полноэкранный режим Выйти из полноэкранного режима

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


Cover Photo by Jamie Matociños on Unsplash

Оцените статью
devanswers.ru
Добавить комментарий