Обновить
32K+
332
Igor Agapov@aio350

JavaScript Developer

27,6
Рейтинг
1 050
Подписчики
Отправить сообщение

Предлагаю учиться у лучших:

// zustand/vanilla.js
const createStoreImpl = (createState) => {
  // состояние
  let state
  // обработчики
  const listeners = new Set()

  // функция обновления состояния
  const setState = (partial, replace) => {
    // следующее состояние
    const nextState = typeof partial === 'function' ? partial(state) : partial
    // если состояние изменилось
    if (!Object.is(nextState, state)) {
      // предыдущее/текущее состояние
      const previousState = state
      // обновляем состояние с помощью `nextState` (если `replace === true` или значением `nextState` является примитив) или нового объекта, объединяющего `state` и `nextState`
      state =
        replace ?? typeof nextState !== 'object'
          ? nextState
          : Object.assign({}, state, nextState)
      // запускаем обработчики
      listeners.forEach((listener) => listener(state, previousState))
    }
  }

  // функция извлечения состояния
  const getState = () => state

  // функция подписки
  // `listener` - обработчик `onStoreChange`
  // см. код `useSyncExternalStoreWithSelector`
  const subscribe = (listener) => {
    listeners.add(listener)
    // отписка
    return () => listeners.delete(listener)
  }

  // функция уничтожения (удаления всех обработчиков)
  const destroy = () => {
    listeners.clear()
  }

  const api = { setState, getState, subscribe, destroy }
  // инициализируем состояние
  state = createState(setState, getState, api)
  // возвращаем методы
  return api
}

export const createStore = (createState) =>
  createState ? createStoreImpl(createState) : createStoreImpl
// zustand/react.js
import useSyncExternalStoreExports from 'use-sync-external-store/shim/with-selector'
import { createStore } from './vanilla.js'

const { useSyncExternalStoreWithSelector } = useSyncExternalStoreExports

export function useStore(api, selector = api.getState, equalityFn) {
  // получаем срез состояния
  const slice = useSyncExternalStoreWithSelector(
    api.subscribe,
    api.getState,
    api.getServerState || api.getState,
    selector,
    equalityFn,
  )
  // и возвращаем его
  return slice
}

const createImpl = (createState) => {
  // получаем методы
  const api =
    typeof createState === 'function' ? createStore(createState) : createState

  // определяем хук
  const useBoundStore = (selector, equalityFn) =>
    useStore(api, selector, equalityFn)

  // не понял, зачем это нужно
  Object.assign(useBoundStore, api)

  return useBoundStore
}

export const create = (createState) =>
  createState ? createImpl(createState) : createImpl
// hooks/useModal.js
import { create } from '../zustand/react'

const useModal = create((set) => ({
  isOpen: false,
  open: () => set({ isOpen: true }),
  close: () => set({ isOpen: false }),
}))

export default useModal
// components/Modal.tsx
import { useEffect, useRef } from 'react'
import { useClickAway } from 'react-use'
import useModal from '../hooks/useModal'

export default function Modal() {
  const modal = useModal()
  const modalRef = useRef(null)
  const modalContentRef = useRef(null)

  useEffect(() => {
    if (!modalRef.current) return

    if (modal.isOpen) {
      modalRef.current.showModal()
    } else {
      modalRef.current.close()
    }
  }, [modal.isOpen])

  useClickAway(modalContentRef, modal.close)

  if (!modal.isOpen) return null

  return (
    <dialog
      style={{
        padding: 0,
      }}
      ref={modalRef}
    >
      <div
        style={{
          padding: '1rem',
          display: 'flex',
          alignItems: 'center',
          gap: '1rem',
        }}
        ref={modalContentRef}
      >
        <div>modal content</div>
        <button onClick={modal.close}>X</button>
      </div>
    </dialog>
  )
}
// App.jsx
import Modal from './components/Modal'
import useModal from './hooks/useModal'

function App() {
  const modal = useModal()

  return (
    <>
      <button onClick={modal.open}>Open modal</button>
      <Modal />
    </>
  )
}

export default App

Дайте определение понятию "красивый")

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

Спасибо, поправил.

Еще один вариант хука useMediaQuery:

import { useCallback, useSyncExternalStore } from "react";

function useMediaQuery(query) {
  const subscribe = useCallback(
    (callback) => {
      const matchMedia = window.matchMedia(query);

      matchMedia.addEventListener("change", callback);
      return () => {
        matchMedia.removeEventListener("change", callback);
      };
    },
    [query]
  );

  const getSnapshot = () => {
    return window.matchMedia(query).matches;
  };

  const getServerSnapshot = () => {
    throw Error("useMediaQuery is a client-only hook");
  };

  return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
}

export default useMediaQuery;

Примеры использования:

const isSm = useMediaQuery("(max-width : 768px)");
const isMd = useMediaQuery(
  "(min-width : 769px) and (max-width : 992px)"
);
const isLg = useMediaQuery(
  "(min-width : 993px) and (max-width : 1200px)"
);
const isXl = useMediaQuery(
  "(min-width : 1201px)"
);

Спасибо, поправил.

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

Простейшая модалка:

<dialog style="padding: 0">
  <div id="modal-box" style="padding: 1rem">
    <div>Modal content</div>
    <button id="close-modal-btn">Close</button>
  </div>
</dialog>
<button id="show-modal-btn">Show modal</button>
const modal = document.querySelector('dialog')
const modalBox = document.getElementById('modal-box')
const showModalBtn = document.getElementById('show-modal-btn')
const closeModalBtn = document.getElementById('close-modal-btn')

let isModalOpen = false

showModalBtn.addEventListener('click', (e) => {
  modal.showModal()
  isModalOpen = true
  e.stopPropagation()
})

closeModalBtn.addEventListener('click', () => {
  modal.close()
  isModalOpen = false
})

document.addEventListener('click', (e) => {
  if (isModalOpen && !modalBox.contains(e.target)) {
    modal.close()
  }
})

Вы правы, спасибо, пофиксил + добавил уникальные сочетания полей для моделей Post и Like.

Хм, надо будет попробовать, но кажется, что идея хорошая.

Действительно, спасибо.

В чем преимущества?

"Распределение не униформное..." - покажите реализацию на TypeScript?
"Результат обрезан до int32..." - это я погорячился, в библиотеке вместо ~~ используется Math.floor(), поправил.

Согласен, поправил. Спасибо за замечание

Действительно, поправил. Спасибо, друг.

Какое такое?) Я специально сделал оговорку, что мы пойдем простым путем. Разумеется, в реальном приложение каждое событие должно обрабатываться отдельно.

Спасибо, друг.

"К недостаткам можно случай, если при размонтировании компонента не удалить события, на которые вы были подписаны, React может продолжать отслеживать изменения, что приведет к утечке памяти и потере производительности."

useEffect(() => {
   // это
    window.addEventListener("online", () => setOnline(true));  
    window.addEventListener("offline", () => setOnline(false));
    return () => {
      // и это - разные обработчики
      // зарегистрированные обработчики удалены не будут
      // получаем утечку памяти при каждом использовании наблюдателя
      window.removeEventListener("online", () => setOnline(true)); 
      window.removeEventListener("offline", () => setOnline(false));
    };
  }, []);

спасибо за подборку

Информация

В рейтинге
308-й
Откуда
Екатеринбург, Свердловская обл., Россия
Дата рождения
Зарегистрирован
Активность

Специализация

Бэкенд разработчик, Фронтенд разработчик
Старший
JavaScript
HTML
React
TypeScript
CSS
Веб-разработка
Node.js
Express
Webpack
Next.js