Я всегда был заинтригован, думая о том, как работают инструменты управления состоянием и что происходит под капотом, но просмотр их исходного кода не очень помогает, потому что большинство из них существуют уже некоторое время и стали очень сложными для понимания ( с точки зрения исходного кода), поэтому, чтобы получить общее представление об инструменте управления состоянием, я выбрал zustand, потому что у него простой API и он относительно новый (выпущен в 2019 году), но все же его довольно сложно понять. текущей кодовой базы, поэтому я откатился к ее первой выпущенной версии zustand v0.0.3 и клонировал ее, так как это самая ранняя версия, в ней не так много функций, но этого достаточно, чтобы понять, что она работает, но сначала давайте возьмем посмотрите на пример его API.

import create from 'zustand'

// earlier versions' of zustand used to export the hook in array
const [ useStore ] = create((set) => ({
    user: null,
    count: 0,
    setUser: (user) => set(currentState => ({ ...currentState, user }))
}))

Функция create принимает функцию, которая возвращает текущее состояние, принимая функцию set в качестве аргумента.

Теперь давайте посмотрим, как данные потребляются/изменяются из хука useStore.

Допустим, есть компонент A и компонент B, использующие хук, что происходит под капотом: каждый раз, когда вызывается хук useStore, этот экземпляр его функции-селектора добавляется в список прослушивателей, прослушивающих изменения в состоянии, и всякий раз, когда изменение состояния инициируется подобно вызову setUser из самого API useStore, все слушатели уведомляются об обновленном состоянии.

export const ComponentA() {
    const user = useStore(state => state.user)
    return (
      <div>{user ? user.name : 'No User!'}</div>
    )
}

export const ComponentB() {
    const { user, count } = useStore(state => ({user: state.user, count: state.count}))
    return (
        // JSX
    )
}

На приведенном ниже рисунке показано, как работает описанный выше механизм: когда вызывается useStore, селектор регистрируется как слушатель, и всякий раз, когда вызывается вызывающее действие, такое как функция обновления состояния, каждый слушатель уведомляется о необходимости обновить свое локальное состояние и вызвать повторный вызов. рендеринг аналогичен тому, что происходит при обновлении состояния в хуке useState. Сами слушатели хранят фрагменты локального состояния в хуке useState, и всякий раз, когда вызывается их функция установки, устанавливается новое состояние, которое запускает повторный рендеринг, но есть несколько вещей, о которых нам нужно позаботиться, когда мы имеем дело с этим, например выбранный срез состояния не обязательно является каким-то значением, это может быть функция, такая как setUser, incrementCount и тому подобное, и обнаружение изменения в функции не является прямым.

Давайте теперь углубимся в реализацию функции создания, функция создания состояния содержит список слушателей и объект глобального состояния (state), который подобен единственному источнику правды в изменении состояния, состояние сохранить в своем свойстве current, которое вызывает функцию инициализации состояния (fn) для получения начального состояния и принимает функцию stateUpdater в качестве аргумента.

Функция stateUpdater — это аргумент set, который мы используем для установки нового состояния. Функция set может либо принимать новое состояние в качестве аргумента, либо функцию, возвращающую новое состояние при вызове, поэтому мы проверяем, выполняется ли слияние. (значение, которое мы передаем для установки) является функцией или значением, если это функция, мы передаем ей текущее состояние, чтобы получить последнее обновленное состояние, и устанавливаем его в текущее свойство нашего глобального состояния, а затем уведомляем наших слушателей о недавнем обновление состояния.

import React from 'react'

function create(fn) {
  let listeners = [];
  let state = {
    current: fn(stateUpdater, () => state.current)
  };

  // useStore: coming soon
  return [useStore]
}

function stateUpdater(merge) {
    if (typeof merge === 'function') {
        merge = merge(state.current)
    }
    state.current = { ...state.current, ...merge };
    listeners.forEach((listener) => listener(state.current));
}

Теперь давайте посмотрим, как работает хук useStore. Прежде всего, мы оцениваем слайс состояния, запрошенный вызовом хука, в зависимости от предоставленного селектора, если таковой имеется, затем мы используем хук useState для хранения слайса состояния в форме функция (функциональная инициализация), затем мы переходим к работе с нашим ping/слушателем, который будет обрабатывать задачу оценки нового состояния, сравнения его с текущим и обновления, как только мы объявим прослушиватель, добавим его в список слушателей, и в качестве очистки удалите привязку слушателя к useStore при размонтировании компонента, теперь, поскольку логика обновления состояния хранится в функции ping, которая хранится глобально в слушателях, любое обновление состояния может быть уведомлено каждым слушателем, вызывая их при изменении состояния.

function create(fn) {
    // ...
    function useStore(selector) {
        const selected = selector ? selector(state.current) : { ...state.current }
        
        const [slice, set] = useState(() => selected);

        useEffect(() => {
          const ping = () => {
            // Get fresh selected state
            let selected = selector
              ? selector(state.current)
              : { ...state.current };

            // perform checks to ensure selected state has changed,
            // if changed, set new state.
            if (currentStateIsNotEqualToNewState) {
              set(() => selected);
            }
          };
          listeners.push(ping);
          return () => {
            listeners = listeners.filter((i) => i !== ping);
            return listeners;
          };
        }, [selector]);
  
        return selected;
    }

    return [useStore]
}

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

Спасибо!