Рубрики
Без рубрики

Реализация USESTATE крючка

Введение Я чувствовал себя неловко в первый раз, когда я читал о крючках (https://reactjsss.org/docs/hooks-intro.html) в реакции. Их внутренняя работа казалась слишком волшебной. Я помню, как смотреть на простой пример …

Автор оригинала: Lev Izraelit.

Вступление

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

function Counter() {
  const [count, setCount] = useState(0)
  return (
    
The count is: {count}
) }

Было ясно, что был примером делать Отказ Вы нажимаете на + Кнопка, и количество увеличивается. Но где была ценность Считать Будучи хранятся и как мы получили правильное значение, хотя 0 был передан каждый раз? Даже когда я начал включать в себя крючки в мои приложения, у меня было несколько четких ответов. Поэтому я начал поиск источников, которые описали, как крючки работают под капотом. Наконец, я решил попробовать и переосмыслить некоторые основные крючки.

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

Классы и штат

Вообще говоря, Государство Включает в себя любое значение, которое меняется со временем, когда эта стоимость необходимо запомнить программой. Для компонентов React Class концепция государства переводится непосредственно в Государство объект. Идея состоит в том, чтобы инкапсулировать все (или, по крайней мере, большинство) изменяющихся значений в одном месте. Мы инициализировали Государство Объект с некоторыми значениями по умолчанию, когда класс создан, а затем измените эти значения косвенно, вызывая SetState Метод:

class Counter extends React.Component {
    constructor() {
      this.state = {
        count: 0
      }
    }

    increment = () => {
      this.setState({
        count: this.state.count + 1
      }) 
    }

    render() {
      return (
        <>
          
count: {this.state.count}
) } }

SetState Метод воссоздает состояние компонента, объединяя существующее состояние с новым объектом, который был передан как аргумент. Если бы мы использовали базу SetState , это будет выглядеть что-то подобное:

  setState(newPartialState) {
    this.state = {
      ...this.state,
      ...newPartialState
    }
    // rerender the component
  }

Функции и состояние

В отличие от объекта или класса, функция не может внутренне поддерживать состояние. Это причина в реакции, что функциональный компонент также называется A без гражданства Функциональный компонент. Поэтому я бы ожидал, что функциональный компонент работает так же, как простой Добавить Функция – Учитывая тот же вход, я бы ожидал, чтобы всегда получить тот же выход. Если бы мне нужно было государству, мне придется создать компонент родительского класса и иметь что Компонент пропустите состояние:

// The Counter functional component will receive 
// the count and a setCount function 
// from a parent class component
const Counter = ({ count, setCount }) => (
  <>
    
count: {count}
) class CounterContainer extends React.Component { // shorthand for using a constructor state = { count: 0 } setCount = (newCount) => { this.setState({ count: newCount }) } render() { return ( ) } }

В некотором смысле Уместите Крюк дает нам путь к Скажите Реагируйте, что нам нужно что-то вроде этого компонента родительского класса, без необходимости создавать его. Мы просто говорим, что мы хотим использовать Состояние и реагирование создаст это состояние для нас.

Функции, которые используют состояние

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

let count = 0;

const Counter = () => (
  <>
    
{count}
)

Это, однако, не совсем работает. Несмотря на то, что ценность Считать меняется, Счетчик Компонент не восстанавливает, чтобы показать новое значение. У нас есть что-то похожее на SetState Вызов, который бы перерезал компонент при стоимости Считать изменения. Мы можем сделать setcount Функция, которая делает только что:

let count = 0

function setCount(newCount) {
  count = newCount
  ReactDOM.render()
}

const Counter = () => (
  <>
    
{count}
)

Это работает! Для обеспечения Считать и setcount Всегда используются вместе, мы можем поставить их внутри объекта. Давайте назовем этот объект MyReact :

const MyReact = {
  count: 0,
  setCount(newCount) {
    this.count = newCount;
    ReactDOM.render()
  }
}

Чтобы сделать вещи даже яснее, давайте создадим Установка Функция, которая возвращает объект с Считать и setcount :

  useCount() {
    return { 
      count: this.count,
      setCount: this.setCount
    }
  }

Далее мы хотели бы позволить абонента Установка пройти начальное значение. Это представило нам проблему. Нам нужно только настроить первоначальное значение в первый раз, когда Установка называется. На любой последующий звонок мы хотели бы использовать существующее значение Установка Отказ Одно решение добавляет УсловныйИсициализирован Переменная. Мы изначально настроим это на ложь и установите это на правда В первый раз, когда Установка называется:

  stateInitialized: false,
  useCount(initialValue) {
    if (!this.stateInitialized) {
      this.count = initialValue;
      this.stateInitialized = true;
    }
    // ...
  }

Теперь, когда мы получили основы, работаем, мы можем сделать MyReact Больше общего переименования Считать Переменная к Государство и имена методов для Уместите и SetState Отказ Кроме того, мы вернемся Государство и SetState В массиве, чтобы позволить легко переименовать:

const MyReact = {
  state: null,
  stateInitialized: false,
  setState(newState) {
    this.state = newState;
    ReactDOM.render(, rootElement);
  },
  useState(initialValue) {
    if (!this.stateInitialized) {
      this.stateInitialized = true;
      this.state = initialValue;
    }
    return [this.state, this.setState];
  }
};

const Counter = () => {
  const [count, setCount] = MyReact.useState(0)
  // ...
}

Мы также можем добавить оказывать Метод MyReact и назовите этот метод вместо того, чтобы звонить Reactom.runder Отказ Это позволит нам сохранить Счетчик Компонент как часть MyReact :

  // ...
  setState(newState) {
    this.state = newState;
    ReactDOM.render(, this.rootElement);
  },
  // ...
  render(component, rootElement) {
    this.component = component;
    this.rootElement = rootElement;
    ReactDOM.render(, this.rootElement);
  }
  // ..

// later 
MyReact.render(Counter)

Многократные переменные состояния

Следующий шаг – включить MyReact управлять несколькими переменными. Первый шаг – заменить один Государство Переменная с массивом из государственных переменных. Теперь нам понадобится какой-то способ узнать, каждый раз SetState называется, который Переменная состояния – это тот, который необходимо изменить. Мы можем достичь этого, полагаясь на призыв к поручению Уместите Отказ Возьмите, например, два последующих вызове ниже:

const MyCounter = () => {
  const [count, setCount] = MyReact.useState(0);
  const [name, setName] = MyReact.useState("");
}

MyReact.Usestate Методы всегда будут выполняться в том же порядке, сначала возвращая значения Count1 , setcount1 , а затем возвращение ценностей Имя , setname Отказ Это будет так, пока MyReact.Usestate это не Вызывается внутри условного блока, где условие не всегда верно или ложно.

Теперь, поскольку у нас есть два или более переменных состояний, каждая переменная состояния должна иметь соответствующую SetState метод. Мы можем достичь этого, используя массив объектов, где объект хранит Государство ценность и соответствующий SetState метод. Мы можем позвонить каждому из объектов A государственная страница и массивы, которые удерживают их statearray Отказ

[{ value: count, setCount }, { value: name, setName }, ...]

Теперь нам нужен способ отслеживать, какой элемент массива используется в любой момент времени. Например, наличие двух звонков на MyReact.Usestate Выше первый звонок должен вернуть [Считать, setcount] а второй звонок должен вернуться [Имя, setname] Отказ Мы можем использовать переменную для отслеживания этого значения. Давайте назовем эту переменную currentstateindex Отказ

currentstateindex будет сброшен до 0 всякий раз, когда любой SetState называется. Когда значение currentstateindex становится равным длине массива, мы создадим новую пару Государство SetState Отказ

const MyReact = {
  stateArr: [],
  currentStateIndex: 0,
  component: null,
  useState(initialValue) {
    // if we reached beyond the last element of the array
    // We will need create a new state
    if (this.currentStateIndex === this.stateArr.length) {
      const statePair = {
        value: initialValue,
        setState(newValue) {
          statePair.value = newValue;
          MyReact.currentStateIndex = 0;
          ReactDOM.render(, rootElement);
        }
      };

      this.stateArr.push(statePair);
    }
    // get the current state and setState before incrementing the index
    const currentStatePair = this.stateArr[this.currentStateIndex];
    this.currentStateIndex += 1;
    return [currentStatePair.value, currentStatePair.setState];
  },
  render(component, rootElement) {
    this.component = component;
    this.rootElement = rootElement;
    ReactDOM.render(, this.rootElement);
  }
};

Пример

Учитывая вышеуказанную реализацию, давайте попробуем привести пример компонента, который использует две переменные состояния:

const Counter = () => {
  const [count1, setCount1] = MyReact.useState(0);
  const [count2, setCount2] = MyReact.useState(0);
  return (
    <>
      
The first count is: {count1}
The second count is: {count2}
) } MyReact.render(Counter)

Вы можете найти песочницу с MyReact и Счетчик Компонент по нажав на эту ссылку

После кода в песочнице они будут начальными значениями Реагировать :

MyReact {  
  stateArr: [],
  currentStateIndex: 0,
  component: null,
}

После первый звонок в Уместите :

const Counter = () => {
  const [count1, setCount1] = MyReact.useState(0); // <--

Значения MyReact будет:

MyReact {  
  stateArr: [{ value: 0, setState: fn() }],
  currentStateIndex: 1,
  component: Counter,
}

После Второй звонок в Уместите :

const Counter = () => {
  const [count1, setCount1] = MyReact.useState(0); 
  const [count2, setCount2] = MyReact.useState(0); // <--

Значения MyReact будет:

MyReact {  
  stateArr: [{ value: 0, setState: fn() }, { value: 0, setState: fn() }],
  currentStateIndex: 2,
  component: Counter,
}

Теперь, если первая + Кнопка нажата, значения MyReact станет:

MyReact {  
  stateArr: [{ value: 1, setState: fn() }, { value: 0, setState: fn() }],
  currentStateIndex: 0,
  component: Counter,
}

Что приведет к Счетчик Быть оказанным снова. На последующие звонки на Уместите , только currentstateindex будет увеличиваться, а существующие элементы statearr будет возвращено.

Заключение

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