Практическое руководство по React Hooks

внешний интерфейс сервер React.js Открытый исходный код

предисловие

существуетReact Conf 2018В ходе встречи Дэн Абрамов представилReact Hooks. Официальное описание

Хуки — это новая функция, позволяющая использовать состояние и другие функции React без написания классов. В настоящее время они находятся в React v16.7.0-alpha. Основной релиз запланирован на первый квартал 2019 года.

Болевые точки

Ниже приведена функция мотивации React Hooks, которая решает некоторые проблемы в существующем React.

Общее состояние между жестким компонентом

В React нет способа привязать повторно используемое поведение к компонентам (например, ссылку на магазин). Если вы использовали его некоторое время, вы можете использоватьrender propsа такжеheight-order componentsКомпоненты решают эту проблему. Но эти шаблоны требуют рефакторинга компонентов при их повторном использовании, что может быть обременительно. если вы используетеReact DevToolsВзгляните на свою программу, и вы обнаружите, что ваши компоненты обернуты различными компонентами, называемыми областями-оболочками, такими как: поставщики, потребители, компоненты более высокого порядка, реквизиты рендеринга и т. д. Здесь есть более глубокая фундаментальная проблема: React нуждается в лучшем способе совместного использования логики состояния.

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

Компоненты становятся все более сложными и трудными для понимания

Нам часто приходится поддерживать компоненты, которые изначально были простыми и со временем превращались в кучу неуправляемой логики с отслеживанием состояния и побочных эффектов. Каждый метод жизненного цикла часто содержит несвязанные комбинации логики. Например, компонент может находиться вcomponentDidMountа такжеcomponentDidUpdateвытащить некоторые данные. а такжеcomponentDidMountМетод также может содержать некоторую несвязанную логику для прослушивателей событий и размонтировать прослушиватели в componentWillUnmount`. Но совершенно несвязанный код объединяется в один метод. склонен к ошибкам и несоответствиям.

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

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

Люди и машины путаются

Помимо усложнения повторного использования и организации кода, мы обнаружили, что классы (classes) может стать большим препятствием для изучения React. Вы должны понимать, как это работает в JavaScript, что сильно отличается от того, как это работает в большинстве языков. Вы должны понимать, как правильно привязывать обработку событий и пока еще не стабильнуюновый синтаксис, код очень подробный. Может быть легко понять свойства (реквизиты), состояние (состояние), поток данных сверху вниз (поток данных сверху вниз), но классы (классы) понять сложно. Разница между компонентами функций и классов в React и когда их использовать приводит к разногласиям даже среди опытных разработчиков React. Используя функцию, которую вы можете использоватьprepackБолее оптимизированный код. Но использование компонентов класса не может быть лучше оптимизировано.

Чтобы решить эти проблемы, хуки позволяют вам использовать больше функций React без классов.

useState

useStateВы можете сделать так, чтобы ваши функциональные компоненты также имели функцию состояния компонентов класса.

Синтаксис использования следующий:

const [state, setState] = useState(initialState);

useStateВозвращает массив, одно из которых является значением состояния, а второе — функцией, которая обновляет состояние

В реальной программе мы можем использовать это так:

function TestUseState() {
  const [count, setCount] = React.useState(0);

  return (
    <div>
      <p>useState api</p>
      <p>Count: {count} <button onClick={() => setCount(count + 1) }>自增</button></p>
    </div>
  )
}

использоватьuseStateОдна вещь, на которую следует обратить внимание при инициализации, — это объект. использоватьsetCountЭто не похоже на компонент классаthis.setStateбудут автоматически объединены в состояние.setCountЗаменяет предыдущее состояние текущим значением. Следующим образом

function TestUseStateObject() {
  const [state, setState] = React.useState({
    count: 0,
    greeting: "Hello, World!",
  });
  const handleAdd = () => {
    setState({
      count: state.count + 1
    })
  }
  console.log('state > ', state)
  return (
    <div>
      <p>useStateObject api</p>
      <p>Count: {state.count} <button onClick={handleAdd}>自增</button></p>
    </div>
  )
}

1543423038609

Мы видим, что состояние заменяется на {count: 1} при нажатии кнопки. Если вы хотите использовать объект в состоянии, вам необходимо деконструировать предыдущее значение при обновлении значения следующим образом:

setState({
      ...state,
      count: state.count + 1
    })

Использование нескольких состояний в функции

function TestMultipleUseState() {
  const [count, setCount] = React.useState(0);
  const [name, setName] = React.useState('john');
  return (
    <div>
      <p>useState api</p>
      <p>Count: {count} - Name: {name}</p>
    </div>
  )
}

Для онлайн-теста перейдите по ссылкеcodepen useState

useEffect

по умолчаниюuseEffectЗапускается после завершения рендеринга, где мы можем получить DOM и обработать другие побочные эффекты. Но у него также есть две разные фазы работы, которые я объясню позже.

function TestUseEffect() {
  const [count, setCount] = React.useState(0);
  
  React.useEffect(() => {
    console.log(`组件被更新,Count: ${count}`);
  });
  
  return (
    <div>
      <p>useEffect api</p>
      <p>Count: {count} <button onClick={() => setCount(count + 1) }>自增</button></p>
    </div>
  )
}

надuseEffectЗапускается после рендеринга каждого компонента, каждый раз, когда мы нажимаем кнопку автоинкремента.

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

useEffect(didUpdate, [])

useEffectВторой параметр — это массив. Когда мы предоставляем второй параметр, изменяется только второй параметрuseEffectбудет казнен. Используя второй параметр, мы можем имитировать компонент класса.componentDidMountфункция жизненного цикла

function TestUseEffectListener() {
  const [count, setCount] = React.useState(0);
  
  React.useEffect(() => {
    console.log('componentDidMount fetch Data...');
  }, []);
  
  return (
    <div>
      <p>TestUseEffectListener</p>
      <p>Count: {count} <button onClick={() => setCount(count + 1) }>自增</button></p>
    </div>
  )
}

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

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

function TestUseEffectUnMount() {
  const [count, setCount] = React.useState(0);
  
  React.useEffect(() => {
    return () => {
      console.log('componentUnmount cleanup...');
    }
  }, []);
  
  return (
    <div>
      <p>TestUseEffectUnMount</p>
    </div>
  )
}

В приведенном выше коде, когда компонентTestUseEffectUnMountБудет выполнен, когда он вот-вот будет уничтоженconsole.log('componentUnmount cleanup...')код

Для онлайн-теста перейдите по ссылкеcodepen useEffect

useContext

useContextпозволяет использовать в функцииcontext, что эффективно решает предыдущуюProvider,ConsumerВопросы, требующие дополнительных компонентов упаковки

Синтаксис использования следующий:

const context = useContext(Context);

Теперь посмотрим на это на практикеuseContextКак использовать, код выглядит следующим образом:

function TestFuncContext() {
  const context = React.useContext(ThemeContext);

  return (
    <div style={context}>TestFuncContext</div>
  )
}

Мы видим, что вышеприведенное напрямую используетReact.useContext(ThemeContext)вы можете получитьcontext, тогда как в предыдущих версиях нужно было получить что-то вроде этого<Consumer>({vlaue} => {})</Consumer>, что значительно упрощает написание кода.

// 之前Consumer的访问方式
function TestNativeContext() {
  return (
    <ThemeContext.Consumer>
      {(value) => {
        return (
          <div style={value}>TestNativeContext</div>
        )
      }}
    </ThemeContext.Consumer>
  );
}

Для онлайн-теста перейдите по ссылкеcodepen useContext

useReducer

useReducerдаuseStateпрограмма условного депонирования. Используйте его, когда у вас есть более ответственные данные.

Синтаксис использования следующий:

const [state, dispatch] = useReducer(reducer, initialState)

Первый параметр — это сокращение для обработки входящего действия.Объявление функции:(state, action) => (). Второй параметр — инициализированная константа состояния.

в возвращаемом значении[state, dispatch], состояние — ваши данные. диспетчеризация может инициировать действие, которое будет обработано в редюсере.

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

Теперь посмотрим на это на практикеuseReducerКак использовать, код выглядит следующим образом:

function TestUseReducer() {
  const [state, setState] = React.useReducer((state, action) => {
    switch(action.type) {
      case 'update':
        return {name: action.payload}
      default:
        return state;
    }
  }, {name: ''});
  
  const handleNameChange = (e) => {
    setState({type: 'update', payload: e.target.value})
  }
  return (
    <div>
      <p>你好:{state.name}</p>
      <input onChange={handleNameChange} />
    </div>
  )
}

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

Для онлайн-теста перейдите по ссылкеcodepen useReducer

useCallback

useCallbackа такжеuseMemoнесколько похоже. Он принимает встроенную функцию и массив, который возвращаетзапоминаниефункция версии.

Синтаксис использования следующий:

const memoizedValue = useMemo(() => computeExpensiveValue(a), [a])

useCallbackПервый параметр — это функция для выполнения некоторых операций и вычислений. Второй параметр - это массив, когда значение в этом массиве меняетсяuseMemoПовторно выполнит и обновит анонимную функцию, на которую ссылаетсяaценность . Это описание может быть немного сложным для понимания. Давайте рассмотрим пример:

function TestUseCallback({ num }) {
  const memoizedCallback = React.useCallback(
    () => {
      // 一些计算
      return num;
    },
    [],
  );
  console.log('记忆 num > ', memoizedCallback())
  console.log('原始 num > ', num);
  return (
    <div>
      <p>TestUseCallback</p>
    </div>
  )
}

_6d371a9a-47a9-4a5c-ac22-28a456b4d4d5

Если мы хотим контролироватьnumОбновление значения снова выполняет некоторые операции и вычисления, мы можем указать второй параметрnumзначение, например:

function TestUseCallback({ num }) {
  const memoizedCallback = React.useCallback(
    () => {
      // 一些计算
      return num;
    },
    [num],
  );
  console.log('记忆 num > ', memoizedCallback())
  console.log('原始 num > ', num);
  return (
    <div>
      <p>TestUseCallback</p>
    </div>
  )
}

Для онлайн-теста перейдите по ссылкеcodepen useCallback

useRef

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

Синтаксис использования следующий:

const refContainer = useRef(initialValue)

useRefВозвращает изменяемый объект, объектcurrentСвойства инициализируются переданным параметром (initialValue). Возвращенный объект будет сохраняться в течение всего времени существования компонента.

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

function TestUseRef() {
  const inputEl = React.useRef(null);
  const onButtonClick = () => {
    // 点击按钮会设置input获取焦点
    inputEl.current.focus(); // 设置useRef返回对象的值
  };
  
  return (
    <div>
      <p>TestUseRef</p>
      <div>
        <input ref={inputEl} type="text" />
        <button onClick={onButtonClick}>input聚焦</button>
      </div>
    </div>
  )
}

useRefВы можете установить возвращаемый объект в других местах, таких как: useEffect, useCallback и т. д.

Для онлайн-теста перейдите по ссылкеcodepen useRef

Оригинальная ссылка

Спасибо за чтение 🙏

Наконец, чтобы сделать объявление, я создалЕженедельный интерфейсПоследние технические статьи и проекты с открытым исходным кодом выходят каждую пятницу Добро пожаловать на подписку