Интенсивное чтение "Как использовать React Hooks для сборки колеса"

внешний интерфейс React.js Promise CSS

1. Введение

прошлая неделяИнтенсивное чтение «React Hooks»Реализовано базовое понимание React Hooks.Может быть вы тоже видели базовый разбор реализации React Hooks (то есть массивов), но сможете ли вы его использовать, если понимаете принцип реализации? То, что вы изучаете, является знанием, но вы используете навыки.Наблюдение за использованием других людей похоже на использование Douyin (вау, вы все еще можете есть так?), у вас всегда будут новые урожаи.

Эта статья применяет эти знания на практике и показывает, как подавляющее большинство программистов используют потенциал React Hooks (какие колеса устроены).

Прежде всего, с точки зрения использования, необходимо понимать, что характеристики React Hooks «очень удобно Подключать все», поэтому можно отслеживать поток данных, сеть или таймер, есть немного RXJS. то есть вы можете использовать React Hooks, превращать компоненты React в: любые изменения в вещах являются источниками ввода, и при изменении этих источников рендеринг компонентов React будет перезапущен, вам нужно только выбрать, какие источники данных (используйте хуки), к которым привязан компонент, а затем просто напишите функцию рендеринга в строке!

2 Интенсивное чтение

После ссылки на некоторые компоненты React Hooks автор классифицировал их в соответствии с их функциями.

Поскольку React Hooks не очень сложен, он не классифицируется по способу технической реализации, ведь когда-нибудь технология станет профессиональной, а классификация по функциям будет иметь непреходящую справочную ценность.

Модификация/прослушивание побочных эффектов DOM

При создании веб-страницы всегда возникают некоторые неприятные вещи, которые, кажется, не имеют ничего общего с компонентами, такие как изменение заголовка страницы (не забудьте изменить заголовок страницы на заголовок по умолчанию), отслеживание изменений размера страницы (не забудьте отменить мониторинг при уничтожении компонента) и подсказка при отключении сети (один слой декораторов собирается сформировать холм). И React Hooks особенно хорошо справляется с такими задачами, делая такие колеса любого размера.

Поскольку React Hooks снижает стоимость использования компонентов более высокого порядка, «жонглирование», которое можно выполнить только за один жизненный цикл, станет очень простым.

Вот несколько примеров:

Изменить заголовок страницы

Эффект: вызывается в компонентеuseDocumentTitleФункция может установить заголовок страницы, а при переключении страниц заголовок страницы сбрасывается на заголовок по умолчанию «Front End Intensive Reading».

useDocumentTitle("个人中心");

Реализация: использовать напрямуюdocument.titleНазначение не может быть проще. Просто дайте заголовок по умолчанию снова при уничтожении.Эту простую функцию можно абстрагировать в функции инструмента проекта, и каждый компонент страницы должен вызывать.

function useDocumentTitle(title) {
  useEffect(
    () => {
      document.title = title;
      return () => (document.title = "前端精读");
    },
    [title]
  );
}

Онлайн-демонстрация

Следите за изменениями размера страницы и отключением сети

Эффект: вызывается в компонентеuseWindowSize, вы можете получить размер страницы и автоматически запускать обновления компонентов при увеличении масштаба браузера.

const windowSize = useWindowSize();
return <div>页面高度:{windowSize.innerWidth}</div>;

Реализация: в основном та же идея, что и в названии, на этот раз изwindow.innerHeightПодождите, пока API напрямую получит ширину и высоту страницы. Обратите внимание, что вы можете использоватьwindow.addEventListener('resize')Отслеживайте изменения размера страницы, звоните в это времяsetValueЭто вызовет повторную визуализацию компонента пользовательского интерфейса, который вызывает сам себя, это так просто!

Наконец, обратите внимание, что при уничтоженииremoveEventListenerВыйдите из прослушивателя.

function getSize() {
  return {
    innerHeight: window.innerHeight,
    innerWidth: window.innerWidth,
    outerHeight: window.outerHeight,
    outerWidth: window.outerWidth
  };
}

function useWindowSize() {
  let [windowSize, setWindowSize] = useState(getSize());

  function handleResize() {
    setWindowSize(getSize());
  }

  useEffect(() => {
    window.addEventListener("resize", handleResize);
    return () => {
      window.removeEventListener("resize", handleResize);
    };
  }, []);

  return windowSize;
}

Онлайн-демонстрация

Динамически внедрять css

Эффект: внедрить класс на страницу и удалить класс при уничтожении компонента.

const className = useCss({
  color: "red"
});

return <div className={className}>Text.</div>;

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

Есть несколько готовых библиотек для сценариев модификации/мониторинга побочных эффектов DOM, и их использование видно из названия:document-visibility,network-status,online-status,window-scroll-position,window-size,document-title.

Помощь компонента

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

Получить ширину и высоту компонента

Эффект: по телефонуuseComponentSizeПолучите ширину и высоту экземпляра ссылки компонента, а при изменении ширины и высоты выполните повторную визуализацию и получите последние значения ширины и высоты.

const ref = useRef(null);
let componentSize = useComponentSize(ref);

return (
  <>
    {componentSize.width}
    <textArea ref={ref} />
  </>
);

Реализация: аналогична мониторингу DOM, но на этот раз заменена использованиемResizeObserverОтслеживайте ссылку на компонент и уничтожайте монитор при уничтожении компонента.

Суть его в отслеживании некоторых побочных эффектов, но через передачу ref мы можем отслеживать и управлять гранулярностью компонента.

useLayoutEffect(() => {
  handleResize();

  let resizeObserver = new ResizeObserver(() => handleResize());
  resizeObserver.observe(ref.current);

  return () => {
    resizeObserver.disconnect(ref.current);
    resizeObserver = null;
  };
}, []);

Онлайн-демонстрация, соответствующая компонентаcomponent-size.

Получить значение, выброшенное компонентом onChange

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

let name = useInputValue("Jamie");
// name = { value: 'Jamie', onChange: [Function] }
return <input {...name} />;

Как видите, это не только не занимает собственное состояние компонента, но и не требует написания вручную callback-функции onChange для обработки, которая сжата в одну строку использования хука.

Реализация: следует примерно догадаться, прочитав здесь, используяuseStateСохраняет значение компонента и выдаетvalueиonChange, мониторonChangeи черезsetValueИсправлятьvalue, чтобы каждый разonChangeКогда запускается повторный рендеринг вызывающего компонента.

function useInputValue(initialValue) {
  let [value, setValue] = useState(initialValue);
  let onChange = useCallback(function(event) {
    setValue(event.currentTarget.value);
  }, []);

  return {
    value,
    onChange
  };
}

Здесь следует отметить, что когда мы улучшаем компонент,Обратный вызов компонента, как правило, не требует уничтожения прослушивателя, а должен прослушиваться только один раз, что отличается от мониторинга DOM., поэтому в большинстве случаев нам нужно использоватьuseCallbackОберните его и передайте пустой массив, чтобы гарантировать, что он прослушивается только один раз навсегда, и нет необходимости отменять регистрацию обратного вызова при уничтожении компонента.

Онлайн-демонстрация, соответствующая компонентаinput-value.

делать анимацию

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

Получить значение от 0 до 1 за определенный период времени

Это самая основная концепция анимации, получение линейно возрастающего значения в течение определенного периода времени.

Эффект: пройтиuseRaf(t)Получите число от 0 до 1, которое постоянно обновляется в течение t миллисекунд.В течение этого периода компонент будет продолжать обновляться, но частота обновления будет контролироваться requestAnimationFrame (пользовательский интерфейс не будет зависать).

const value = useRaf(1000);

Реализация: Можно более подробно написать, вот краткое описание. использоватьrequestAnimationFrameУчитывая значение от 0 до 1 в заданное время, тогда каждый раз, когда вы обновляете, вам просто нужно оценить отношение текущей точки времени обновления к общему времени, а затем сделать знаменатель, а числитель равен 1.

Онлайн-демонстрация, соответствующая компонентаuse-raf.

Эластичная анимация

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

Фактический метод вызова, как правило, сначала черезuseStateПолучите значение, а затем оберните значение через функцию анимации, чтобы компонент обновлялся от одного до N раз, а полученное значение также изменялось с правилами функции анимации, и, наконец, это значение стабилизировалось до конец входного значения (как в примере50).

const [target, setTarget] = useState(50);
const value = useSpring(target);

return <div onClick={() => setTarget(100)}>{value}</div>;

Реализация: чтобы добиться эффекта анимации, вам нужно полагаться наreboundбиблиотеку, которая может реализовать функцию дизассемблирования целевого значения в функцию, которая соответствует процессу функции эластичной анимации, то нам нужно использовать React Hooks для первого вызова целевого значения.spring.setEndValueзапускать события анимации, а вuseEffectПроведите разовый мониторинг вsetValueВот и все.

Самое удивительноеsetTargetсвязьuseSpringПересчитайте часть эластичной анимации, которая выполняетсяuseEffectВторой параметр реализует:

useEffect(
  () => {
    if (spring) {
      spring.setEndValue(targetValue);
    }
  },
  [targetValue]
);

То есть при изменении целевого значения будет выполняться новый раунд повторного рендеринга, поэтомуuseSpringНет необходимости следить за звонящимsetTarget, это нужно только слушатьtargetизменения и грамотное использованиеuseEffectВторой параметр может сделать больше с меньшими затратами.

Онлайн-демонстрация

Твин анимация

После понимания принципа эластичной анимации анимация Tween становится еще проще.

Эффект: пройтиuseTweenПолучите значение, которое изменяется от 0 до 1, кривая анимации для этого значенияtween. Как видите, поскольку диапазон значений фиксирован, нам не нужно задавать начальное значение.

const value = useTween();

Реализация: черезuseRafПолучите линейно возрастающее значение (интервал также от 0 до 1), а затем передайтеeasingБиблиотека сопоставляет его со значением от 0 до 1. Здесь используется связь хука, вызывающего хук (черезuseRafводить машинуuseTween), а также выводы в других местах.

const fn: Easing = easing[easingName];
const t = useRaf(ms, delay);

return fn(t);

Отправить запрос

С помощью хуков любое обещание запроса можно инкапсулировать в виде объекта со стандартными состояниями: загрузка, ошибка, результат.

Универсальная оболочка Http

Эффект: пройтиuseAsyncРазберите промис на три объекта: загрузка, ошибка и результат.

const { loading, error, result } = useAsync(fetchUser, [id]);

Реализация: установить загрузку в начале промиса, установить результат после конца и установить ошибку, если есть ошибка.Здесь вы можете обернуть объект запроса какuseAsyncStateЧтобы справиться с этим, он не будет выпущен здесь.

export function useAsync(asyncFunction) {
  const asyncState = useAsyncState(options);

  useEffect(() => {
    const promise = asyncFunction();
    asyncState.setLoading();
    promise.then(
      result => asyncState.setResult(result);,
      error => asyncState.setError(error);
    );
  }, params);
}

Конкретный код может относиться кreact-async-hook, рекомендуется только понять принцип этой функции, и необходимо рассмотреть конкретную реализацию, потому что есть некоторые граничные условия, такие как компонент isMounted. Результат может быть запрошен соответственно.

Request Service

Бизнес-уровень обычно абстрагируетrequest serviceСделайте абстракцию унифицированной выборки (например, унифицированный URL-адрес или унифицированную реализацию сокета и т. д.). Предположим, что предыдущий способ сравнения low:

async componentDidMount() {
  // setState: 改 isLoading state
  try {
    const data = await fetchUser()
    // setState: 改 isLoading、error、data
  } catch (error) {
    // setState: 改 isLoading、error
  }
}

Позже, когда запрос будет помещен в редукс, способ инъекции через коннект будет немного изменен:

@Connect(...)
class App extends React.PureComponent {
  public componentDidMount() {
    this.props.fetchUser()
  }

  public render() {
    // this.props.userData.isLoading | error | data
  }
}

В конце концов, вы обнаружите, что хуки лаконичны и ясны:

function App() {
  const { isLoading, error, data } = useFetchUser();
}

иuseFetchUserИспользуя вышеуказанный пакетuseAsyncЛегко написать:

const fetchUser = id =>
  fetch(`xxx`).then(result => {
    if (result.status !== 200) {
      throw new Error("bad status = " + result.status);
    }
    return result.json();
  });

function useFetchUser(id) {
  const asyncFetchUser = useAsync(fetchUser, id);
  return asyncUser;
}

заполните форму

React Hooks отлично подходят для форм, особенноantd formЕсли вы поддерживаете версию Hooks, ее будет намного проще использовать:

function App() {
  const { getFieldDecorator } = useAntdForm();

  return (
    <Form onSubmit={this.handleSubmit} className="login-form">
      <FormItem>
        {getFieldDecorator("userName", {
          rules: [{ required: true, message: "Please input your username!" }]
        })(
          <Input
            prefix={<Icon type="user" style={{ color: "rgba(0,0,0,.25)" }} />}
            placeholder="Username"
          />
        )}
      </FormItem>
      <FormItem>
        <Button type="primary" htmlType="submit" className="login-form-button">
          Log in
        </Button>
        Or <a href="">register now!</a>
      </FormItem>
    </Form>
  );
}

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

Компоненты форм для хуков

Эффект: пройтиuseFormStateПолучите значение формы и предоставьте сериюПомощь компонентаМетоды управляют состоянием компонента.

const [formState, { text, password }] = useFormState();
return (
  <form>
    <input {...text("username")} required />
    <input {...password("password")} required minLength={8} />
  </form>
);

выше может пройтиformStateПолучите значение формы в любое время и некоторую информацию для проверки черезpassword("pwd")перейти кinputкомпонент, пусть этот компонент достигает контролируемого состояния, а тип вводаpasswordтип, ключ формыpwd. и может увидеть использованиеformБудучи нативными тегами, это улучшение формы совершенно не связано.

Реализация: внимательно соблюдайте структуру, найти не сложно, нужно только совместитьИдею «получения значения, выброшенного компонентом onChange» в разделе помощи компонента, можно легко понять.text,passwordкак это действует наinputкомпонент и получить его входное состояние.

Проще говоря, просто объедините эти состояния вместе и передайтеuseReducerагрегировать вformStateЭтого можно достичь.

Для простоты рассмотрим толькоinputУлучшение, исходный код требует всего 30 строк:

export function useFormState(initialState) {
  const [state, setState] = useReducer(stateReducer, initialState || {});

  const createPropsGetter = type => (name, ownValue) => {
    const hasOwnValue = !!ownValue;
    const hasValueInState = state[name] !== undefined;

    function setInitialValue() {
      let value = "";
      setState({ [name]: value });
    }

    const inputProps = {
      name, // 给 input 添加 type: text or password
      get value() {
        if (!hasValueInState) {
          setInitialValue(); // 给初始化值
        }
        return hasValueInState ? state[name] : ""; // 赋值
      },
      onChange(e) {
        let { value } = e.target;
        setState({ [name]: value }); // 修改对应 Key 的值
      }
    };

    return inputProps;
  };

  const inputPropsCreators = ["text", "password"].reduce(
    (methods, type) => ({ ...methods, [type]: createPropsGetter(type) }),
    {}
  );

  return [
    { values: state }, // formState
    inputPropsCreators
  ];
}

Приведенные выше 30 строк кода реализуютinputНастройки типа этикетки, мониторvalue onChange, со временем объединившись в большоеvaluesв видеformStateвозвращение. Прочитав это, вы обнаружите, что применение React Hooks неотделимо от того же принципа, особенно сбора информации о компонентах, которая выполняется путем деконструкции, а затем агрегируется внутри хуков для выполнения основных функций компонента формы.

На самом деле полное колесо также необходимо учитыватьcheckbox radioпроблемы совместимости и проверки, эти идеи похожи, можно увидеть конкретный исходный кодreact-use-form-state.

имитация жизненного цикла

Иногда API React15 весьма полезен, и почти полный набор можно смоделировать с помощью React Hooks.

componentDidMount

Эффект: пройтиuseMountФункция обратного вызова, которая выполняется после получения цикла монтирования.

useMount(() => {
  // quite similar to `componentDidMount`
});

выполнить:componentDidMountЭквивалентноuseEffectОбратный вызов (при выполнении только один раз), поэтому функцию обратного вызова можно вызвать напрямую.

useEffect(() => void fn(), []);

componentWillUnmount

Эффект: пройтиuseUnmountФункция обратного вызова, которая выполняется после получения цикла размонтирования.

useUnmount(() => {
  // quite similar to `componentWillUnmount`
});

выполнить:componentWillUnmountЭквивалентноuseEffectВозвращаемое значение функции обратного вызова (при выполнении только один раз), поэтому возвращаемое значение функции обратного вызова может быть выброшено напрямую.

useEffect(() => fn, []);

componentDidUpdate

Эффект: пройтиuseUpdateПолучите функцию обратного вызова, которая выполняется только после цикла didUpdate.

useUpdate(() => {
  // quite similar to `componentDidUpdate`
});

выполнить:componentDidUpdateЭквивалентноuseMountЛогика выполняется каждый раз, кроме первой инициализации. Поэтому используйте флаг mouting (судя по начальному состоянию) + неограниченные параметры, чтобы гарантировать выполнение каждого повторного рендеринга.

const mounting = useRef(true);
useEffect(() => {
  if (mounting.current) {
    mounting.current = false;
  } else {
    fn();
  }
});

Force Update

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

const update = useUpdate();

Исполнение: мы знаемuseStateЭлемент с индексом 1 используется для обновления данных, и даже если данные не изменятся, компонент будет обновлен при его вызове, поэтому мы можем вернуть неизмененное значение.setValue, так что его функцией остается только компонент обновления.

const useUpdate = () => useState(0)[1];

заgetSnapshotBeforeUpdate, getDerivedStateFromError, componentDidCatchВ настоящее время над хуками нельзя издеваться.

isMounted

React давно предоставил этот API, но позже он был удален, потому что это можно было сделать черезcomponentWillMountиcomponentWillUnmountПолучить. Начиная с React Hooks, поддержка isMount — дело нескольких минут.

Эффект: пройтиuseIsMountedполучитьisMountedгосударство.

const isMounted = useIsMounted();

Реализация: если вы видите это, значит, вы уже знакомы с этой подпрограммой.useEffectПри первом вызове значение устанавливается равным true, а при уничтожении компонента возвращается значение false.Обратите внимание, что второй параметр можно добавить в виде пустого массива для оптимизации производительности.

const [isMount, setIsMount] = useState(false);
useEffect(() => {
  if (!isMount) {
    setIsMount(true);
  }
  return () => setIsMount(false);
}, []);
return isMount;

Онлайн-демонстрация

сохранять данные

В предыдущей статье упоминались встроенные React Hooks.useReducerВы можете имитировать поведение редуктора Redux, единственное, что вам нужно добавить, это сохранить данные. Мы рассматриваем минимальную реализацию, которая представляет собой глобальную часть Store + Provider.

Глобальный магазин

Эффект: пройтиcreateStoreСоздайте глобальный магазин, а затем передайтеStoreProviderбудетstoreвнедряется в дочерние компонентыcontext, и, наконец, получить и использовать два хука:useStoreиuseAction:

const store = createStore({
  user: {
    name: "小明",
    setName: (state, payload) => {
      state.name = payload;
    }
  }
});

const App = () => (
  <StoreProvider store={store}>
    <YourApp />
  </StoreProvider>
);

function YourApp() {
  const userName = useStore(state => state.user.name);
  const setName = userAction(dispatch => dispatch.user.setName);
}

Реализация: Реализация этого примера может быть отдельной статьей, поэтому автор анализирует ее с точки зрения хранения данных.StoreProviderреализация.

Да, хуки не решают проблему провайдера, поэтому глобальное состояние должно иметь провайдера, но этот провайдер может использовать встроенный в ReactcreateContextПросто сделать:

const StoreContext = createContext();

const StoreProvider = ({ children, store }) => (
  <StoreContext.Provider value={store}>{children}</StoreContext.Provider>
);

остальноеuseStoreВопрос о том, как получить постоянный магазин, используйте его здесьuseContextи только что созданный объект Context:

const store = useContext(StoreContext);
return store;

Дополнительный исходный код может ссылаться наeasy-peasy, эта библиотека написана на основе redux и предоставляет набор Hooks API.

Упакуйте оригинальную библиотеку

Придется ли переписывать все библиотеки после выхода React Hooks? Конечно нет, давайте посмотрим, как трансформируют другие библиотеки.

RenderProps to Hooks

возьми это здесьreact-powerplugПример.

Например, есть библиотека renderProps, которую я надеюсь трансформировать в использование хуков:

import { Toggle } from 'react-powerplug'

function App() {
  return (
    <Toggle initial={true}>
      {({ on, toggle }) => (
        <Checkbox checked={on} onChange={toggle} />
      )}
    </Toggle>
  )
}
↓ ↓ ↓ ↓ ↓ ↓
import { useToggle } from 'react-powerhooks'

function App() {
  const [on, toggle] = useToggle()
  return <Checkbox checked={on} onChange={toggle} />
}

Эффект: Если бы я былreact-powerplugМейнтейнер, как поддерживать React Hook с минимальными затратами?Честно говоря, это невозможно сделать за один шаг, но можно добиться за два шага.

export function Toggle() {
  // 这是 Toggle 的源码
  // balabalabala..
}

const App = wrap(() => {
  // 第一步:包 wrap
  const [on, toggle] = useRenderProps(Toggle); // 第二步:包 useRenderProps
});

Реализация: сначала объясните, почему нам нужно обернуть два слоя, во-первых, хуки должны соответствовать спецификациям React, мы должны написатьuseRenderPropsФункция имеет формат, соответствующий хукам. **Вопрос в том, как заставить Toggle отображатьonиtoggle? ** Обычный способ не должен быть в состоянии получить это, так что это следующий лучший способ сделатьuseRenderPropsПолученный Toggle передаетсяwrap,позволятьwrapСоздайте среду выполнения RenderProps, чтобы получитьonиtoggleПосле этого позвонитеuseRenderPropsВнутреннийsetArgsфункция, пустьconst [on, toggle] = useRenderProps(Toggle)Реализуйте кривую, чтобы спасти страну.

const wrappers = []; // 全局存储 wrappers

export const useRenderProps = (WrapperComponent, wrapperProps) => {
  const [args, setArgs] = useState([]);
  const ref = useRef({});
  if (!ref.current.initialized) {
    wrappers.push({
      WrapperComponent,
      wrapperProps,
      setArgs
    });
  }
  useEffect(() => {
    ref.current.initialized = true;
  }, []);
  return args; // 通过下面 wrap 调用 setArgs 获取值。
};

так какuseRenderPropsбудет предшествоватьwrapВыполняется, поэтому обертки сначала получат Toggle,wrapвызов непосредственно во время выполненияwrappers.pop()Вы можете получить объект Toggle. Затем создайте среду выполнения RenderProps:

export const wrap = FunctionComponent => props => {
  const element = FunctionComponent(props);
  const ref = useRef({ wrapper: wrappers.pop() }); // 拿到 useRenderProps 提供的 Toggle
  const { WrapperComponent, wrapperProps } = ref.current.wrapper;
  return createElement(WrapperComponent, wrapperProps, (...args) => {
    // WrapperComponent => Toggle,这一步是在构造 RenderProps 执行环境
    if (!ref.current.processed) {
      ref.current.wrapper.setArgs(args); // 拿到 on、toggle 后,通过 setArgs 传给上面的 args。
      ref.current.processed = true;
    } else {
      ref.current.processed = false;
    }
    return element;
  });
};

Приведенная выше ссылка на схему реализацииreact-hooks-render-props, если надо, то можно взять и использовать напрямую, а можно сослаться на идеи реализации.У автора большая дыра в мозгу.

Hooks to RenderProps

Что ж, если вы хотите, чтобы хуки поддерживали RenderProps, вы должны поддерживать оба набора синтаксисов.

Эффект: один набор кода поддерживает и хуки, и RenderProps.

Реализация: на самом деле удобнее всего инкапсулировать хуки как RenderProps, поэтому мы используем хуки для написания основного кода, предполагая, что мы пишем самый простойToggle:

const useToggle = initialValue => {
  const [on, setOn] = useState(initialValue);
  return {
    on,
    toggle: () => setOn(!on)
  };
};

Онлайн-демонстрация

затем пройтиrender-propsЭта библиотека упрощает инкапсуляцию компонента RenderProps:

const Toggle = ({ initialValue, children, render = children }) =>
  renderProps(render, useToggle(initialValue));

Онлайн-демонстрация

фактическиrenderPropsВторой параметр этого компонента, в виде компонента Class React, получаетthis.state, теперь мы меняем наuseToggleВозвращенный объект также можно понимать какstate, используйте механизм Hooks для управления повторной визуализацией компонента Toggle, чтобы дочерний компонент повторно отображался.

Инкапсулирует библиотеки, изначально улучшенные setState.

Хуки также особенно подходят для инкапсуляции библиотек, которые изначально действуют на setState, таких какimmer.

useStateХотя нетsetState, но его можно понимать как управление компонентами более высокого порядкаsetState, мы можем полностью инкапсулировать пользовательскийuseState, то встроенная параsetStateОптимизация.

Например, синтаксис для immer — черезproduceОбертывание, проксирование изменяемого кода в неизменяемый через прокси:

const nextState = produce(baseState, draftState => {
  draftState.push({ todo: "Tweet about it" });
  draftState[1].done = true;
});

что этоproduceпутем инкапсуляцииuseImmerпрятаться:

function useImmer(initialValue) {
  const [val, updateValue] = React.useState(initialValue);
  return [
    val,
    updater => {
      updateValue(produce(updater));
    }
  ];
}

Как пользоваться:

const [value, setValue] = useImmer({ a: 1 });

value(obj => (obj.a = 2)); // immutable

3 Резюме

В этой статье перечислены следующие способы использования React Hooks и идеи их реализации:

  • Модификация/прослушивание побочных эффектов DOM.
  • Помощник по компонентам.
  • Делай анимацию.
  • Отправить запрос.
  • Заполните форму.
  • Имитация жизненного цикла.
  • сохранять данные.
  • Упакуйте оригинальную библиотеку.

Продолжение дополнений приветствуется.

4 Еще обсуждения

Адрес обсуждения:Интенсивное чтение «Как создавать колеса с помощью React Hooks» · Выпуск №112 · dt-fe/weekly

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