Как стать автором
Обновить
283
52
Igor Agapov @aio350

JavaScript Developer

Отправить сообщение

Тут есть о чем подумать. Опыт показывает, что клиентских компонентов в приложении гораздо больше, чем серверных, поэтому приходится постоянно писать use client. С другой стороны, серверные компоненты все-таки являются первичными с точки зрения рендеринга в Next.js. На подходе Turbopack и React Compiler, возможно, кто-то из них будет автоматически определять клиентские компоненты. А для удаления use client, скорее всего, выпустят codemod.

Нет, я его просто дополнил)

Цитата из официальной документации React:
"React - это библиотека. Она позволяет использовать компоненты, но не предписывает, как выполнять маршрутизацию или получение данных. Для разработки приложения с помощью React рекомендуется использовать клиент-серверный фреймворк React, такой как Next.js или Remix".

Нет, это перевод статьи из блога Vercel, подразумевается использование Next.js. В SPA у вас весь код работает на клиенте, нет необходимости специально помечать его с помощью 'use client'.

Мне очень понравилась книга Николая Кукушкина "Хлопок одной ладонью".

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

Речь о том, что SEO-теги возвращаются на первый запрос.

Функция для выполнения переданной функции с помощью веб-воркера:

/**
  @example
  const expensiveFn = () => {
    let result = 0
    for (let i = 0; i < 10000; i++) {
      for (let j = 0; j < 700; j++) {
        for (let k = 0; k < 300; k++) {
          result = result + i + j + k
        }
      }
    }
    return result
  }
  worker(expensiveFn).then(console.log) // 11546850000000
 */
const worker = (fn: Function) => {
  const worker = new Worker(
    URL.createObjectURL(
      new Blob([`postMessage((${fn})())`], {
        type: 'application/javascript; charset=utf-8',
      }),
    ),
  )
  return new Promise((res, rej) => {
    worker.onmessage = ({ data }) => {
      res(data)
      worker.terminate()
    }
    worker.onerror = (err) => {
      rej(err)
      worker.terminate()
    }
  })
}

export default worker

Спасибо за статью, но не помешала бы ссылка на репозиторий с кодом проекта.

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

// 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()
  }
})
1
23 ...

Информация

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

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

Backend Developer, Frontend Developer
Senior
JavaScript
HTML
React
TypeScript
CSS
Web development
Node.js
Express
Webpack
NextJS