Ответы пользователя по тегу React
  • Ошибка Failed prop type: Invalid prop `children` supplied to `ForwardRef(Box)`, expected a ReactNode. Что я делаю не так?

    Aetae
    @Aetae
    Тлен
    React так не умеет. Сам механизм react делает такое невозможным.
    Все компоненты синхронны(кроме lazy, но это совсем другое).
    Ты должен свою асинхронную функцию вызвать в useEffect, а пока она грузится показывать какой-нить лоадер.
    Ответ написан
    1 комментарий
  • Ошибка Error: Hydration failed because the initial UI does not match what was rendered on the server. Как исправить?

    Aetae
    @Aetae
    Тлен
    Очевидная проблема в том, что ты рендеришь страницу по разному в зависимости от свойств экрана юзера, однако сервер не знает ничего об юзерском экране, потому результат SSR и результат на клиенте - разный, что и вызывает ошибки гидрации.

    Красивого решения тут нет, только разные костыли, например:
    1. Первый раз рендерить компонент всегда одинаково, а потом уже обновлять по useEffect в зависимости от экрана. Простое решение которое сразу заработает, однако может привести к кратковременном миганию на клиенте при загрузке.
    1а. Вычислять по userAgent что у клиента мобила\планшет и первым рисовать наиболее вероятный вариант, а потом уже по useEffect использовать точный, если не угадали. Тоже самое но есть шанс что угадаем.
    2. Рисовать все варианты на странице, а скрывать тупо классами css не используя этой либы. Тоже сразу заработает, но на странице куча мусора.
    3. ?
    Ответ написан
    Комментировать
  • Возможно ли изменить поведение contentEditable?

    Aetae
    @Aetae
    Тлен
    Поменять поведение - нельзя.
    Отслеживать изменения и переписывать - можно, но не нужно.
    Ответ написан
    Комментировать
  • Как сделать фильтрацию?

    Aetae
    @Aetae Куратор тега JavaScript
    Тлен
    Чтоб сделать фильтрацию просто делаешь так:
    const [selected, setSelected] = useState('all');
    
      let filterdTodos;
      switch(selected) {
        case 'all':
          filterdTodos = todos;
          break;
        case 'checked':
          filterdTodos = todos.filter(t => t.checked);
          break;
        case 'notChecked':
          filterdTodos = todos.filter(t => !t.checked);
          break;
      }

    Или так, с оптимизацией:
    const [selected, setSelected] = useState('all');
    
      const filterdTodos = useMemo(() => {
        switch(selected) {
          case 'all':
            return todos;
          case 'checked':
            return todos.filter(t => t.checked);
          case 'notChecked':
            return todos.filter(t => !t.checked);
        }
      }, [selected, todos])

    <select className="todos__filter" onChange={(event) =>setSelected(event.target.value)}>
    ...
    {filterdTodos.map(...)}
    Ответ написан
  • При сборке к имени картинок добавляется хеш и картинки не отображаются. Как указывать путь к картинкам?

    Aetae
    @Aetae
    Тлен
    React - это тебе не Vue, он не умеет из коробки угадывать, что картинки надо импортировать.

    Для каждой картинки надо сделать руками:
    import img from './assets/img.jpeg'
    или использовать glob import.

    Ну либо положить картинки в public и брать их как есть по прямым путям относительно корня без всяких хэшей.

    Подробнее.
    Ответ написан
    Комментировать
  • Как использовать один компонент с разными типами данных?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Наверное ты хочешь этого:
    type Props = { data?: ITemplate } | {}
    const CreateTemplateForm: FC<Props> = (props) => {
      if ('data' in props && props.data) {
        
        return ...;
      }
    
      return ...;
    }
    Ответ написан
    5 комментариев
  • Как запустить реакт код без локального сервера или как его получить?

    Aetae
    @Aetae
    Тлен
    Забудь о фронте без локального сервера. Это время ушло.
    Сейчас для локальных файлов столько ограничений, что работать с этим даже на чистом html уже толком невозможно.
    Ответ написан
  • Для чего мемоизируют аутентификацию?

    Aetae
    @Aetae
    Тлен
    На самом деле useMemo нужен, чтобы при передаче значения как prop в низлежащий memo компонент не происходила перерисовка оного если не было изменений(+при передаче как зависимость в другой хук, тот не срабатывал заново).
    Стоит упустить хотя-бы одно место требующее useMemo и пойдёт водопадом перерисовка на каждый чих каждого компонента по всему дереву вниз. И именно это является основной причиной тормозов в React, а не какие-то там мифические сложные вычисления.

    Официальная позиция React: "говнокодь сейчас, оптимизируй потом", действительно предполагает использование useMemo "только в узких местах" и нигде больше. Прикол в том, что с таким подходом при разрастании проекта никакого "узкого места" просто нет, тормозить начинает просто потому, что складываются тысячи микротормозов от тысяч перерисовок тысяч компонентов: наступает то самое "потом" и тут придётся переписывать с useMemo чуть ли не весь проект, чтоб снизить эти тормоза.
    Именно по этому в реальной работе useMemo стараются таки использовать заранее в каждом месте, где оно потенциально нужно. Некоторые радикальные философии вообще предполагают использование useMemo просто всегда, без исключений.:)
    Ответ написан
    3 комментария
  • Как компонент реакта понимает, что к нему привязали хук?

    Aetae
    @Aetae
    Тлен
    Простой ответ: глобальные переменные.:)
    React просто перед самим запуском устанавливает глобальную (условно) переменную указывающую на текущий исполняющийся компонент, на которую и смотрит в сою очередь хук. Именно потому хуки нельзя использовать вне компонента.
    Вот тут я упрощённо изобразил, что дальше происходит внутри useState.

    Конечно всё намного сложнее, но основная суть именно такова. Подробнее - уже в исходники.
    Ответ написан
    2 комментария
  • Как при нажатии кнопки выводить поэтапно строки которые значение увеличивается на 1?

    Aetae
    @Aetae Куратор тега JavaScript
    Тлен
    Для начала забудем о react и поговорим о javascript: javascript риусет ui в одном потоке, потому while - это атомарная операция. На экране ничего не отобразится пока while не закончит своё действие. Т.е. выведя 10 строк через while - ты сразу увидишь их на экране, они не будут появляться по одной. Если, конечно, не подключишь асинхронность с async и await внутри цикла.

    Теперь к react: отображение нескольких строк осуществляется с помощью массива:
    const lines = [1, 2, 3];
    
    return lines.map(line => (
      <p> {line} </p>
    ));


    Если ты хочешь, чтоб при клике добавлялась строчка, то состояние этого массива надо отслеживать, т.е. напримр положить его в state:
    function Component() {
      const [lines, setLines] = useState([1, 2, 3]);
      const addLine = useCallback(() => {
        setLines(lines => [...lines, lines.length+1])
      });
    
      return(
        <>
          <button onClick={addLine} className="button">
            addLine
          </button>
          {lines.map(line => (
            <p> {line} </p>
          ))}
        </>
    }


    Если ты хочешь, чтоб при клике постепенно добавлялась 10 строчек с задержкой, то это можно сделать с помощью setTimeout(тут я обернул его в Promise для простоты и наглядности):
    const delay = (ms) => new Promise(r => setTimeout(r, ms))
    
    function Component() {
      const [lines, setLines] = useState([]);
      const addLines = useCallback(async () => {
        let i = 0;
        while(i++ < 10) {
          await delay(1000);  
          setLines(lines => [...lines, i])
        }
      });
      return(
        <>
          <button onClick={addLines} className="button">
            addLine
          </button>
          {lines.map(line => (
            <p> {line} </p>
          ))}
        </>
      );
    }
    Ответ написан
    Комментировать
  • Почему при сборке библиотеки и подключения ее, выдает ошибку?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Не надо ничего "копировать из dist". В node_modules должен быть модуль, т.е. в первую очередь файлик package.json, во вторую - всё что в нём указано по тем путям которые там указаны.
    Вот когда ты делаешь npm link - открой node_modules и посмотри как должна выглядеть твоя либа(только без исходников).
    Когда ты сделаешь нормальный npm publish - токчно также всё просто скопируется в npm.
    Ответ написан
    Комментировать
  • Как работает функция обновления в React?

    Aetae
    @Aetae
    Тлен
    Вот так утрировано выглядит внутри функция useState:
    function useState(initialValue) {
      // подсчитываем вызовы useState в компоненте
      component.useStateCount++;
    
      // если уже был вызов этого useState(т.е. это не первая отрисовка)
      if (component.useStateCount in component.useStateCache) 
        // возвращаем результат из кэша
        return component.useStateCache[component.useStateCount];
    
      // если первый вызов - подготавливаем ответ вида [state, setState]
      const useStateResult = [
        initialValue, 
        function setState(callbackOrValue) {
          // если аргумент setState - функция
          if (typeof callbackOrValue === 'function') {
            // вызываем её с предыдущем значением в качестве аргумента и присваиваем результат вызова в state
            useStateResult[0] = callbackOrValue(useStateResult[0]);
          } else {
            // иначе просто присваиваем аргумент в state
            useStateResult[0] = callbackOrValue;
          }
    
          // вызов обновления компонента
          component.updateComounent(); 
        }
      ];
    
      // добавляем в кэш
      component.useStateCache[component.useStateCount] = useStateResult;
    
      // возвращаем
      return useStateResult;
    }

    Стало понятней?
    Ответ написан
    1 комментарий
  • Как скрыть элемент страницы с помощью tampermonkey (userscript) в iframe, содержимое которого сделано через react?

    Aetae
    @Aetae Куратор тега JavaScript
    Тлен
    Проще всего это делается через css селектор атрибутов вида [class^="chat__root__"] . Заметьте я использовал не полное имя класса, а только начало и модификатор ^= указывающий сверять только начало(если класс не первый, то можно использовать *=). Почему? Потому что "непонятные буковки" в конце - это генерируемый при сборке хэш, который поменяется при выпуске следующей версии фронта и если вы к нему привяжитесь - ваш скрипт очень скоро перестанет работать.

    Если фрейм с другого адреса, то надо создать этот userscript именно для этого адреса и уже применять там:
    // @run-at document-start
    
    function addCss(css, root = window) {
      const style = root.document.createElement('style');
      style.innerHTML = css;
      (root.document.head || root.document.documentElement).append(style);
      return style;
    } 
    
    const css = `
      [class^="chat__root__"] {
        dislpay: none !important;
      }
    `;
    
    addCss(css);

    Если с этого же, то придётся заморочиться:
    // @run-at document-start
    
    const css = `
      [class^="chat__root__"] {
        dislpay: none !important;
      }
    `;
    
    // отлавливаем загрузку фреймов
    new MutationObserver(() => {
      document.querySelectorAll('iframe:not([us-processed]').forEach((iframe) => {
        if (isAccessibleFrame(iframe)) {
          // добавляем наш css
          addCss(css, iframe.contentWindow);
          
          // на случай если содержимое фрейма перепишут позднее
          iframe.addEventListener('load', () => addCss(css, iframe.contentWindow));
        }
        // помечаем что обработали этот фрем, чтоб не повторять
        iframe.setAttribute('us-processed', true);
      });
    }).observe(document, {childList: true, subtree: true});
    
    // проверяем что содержимое фрейма доступно 
    function isAccessibleFrame(iframe) {
      try {
        iframe.contentWindow.test
        return true
      } catch (e) {
        return false
      }
    }
    Ответ написан
  • Как правильно настроить swiper в react?

    Aetae
    @Aetae Куратор тега JavaScript
    Тлен
    Компонент Swiper не ждёт свойства spaceBetween. Вы уверены, что оно хоть что-то делает?

    Если это баг типизации Swiper и на самом деле он такое свойство ждёт, то вам следует репортить этот баг автору компонента (предварительно обновившись до последней версии).

    В крайнем случае можно прибегнуть к declaration merging, но это вредный костыль:
    declare module 'путь до типов компонента Swiper' {
      interface SwiperProps {
        spaceBetween: number;
      }
    }
    Ответ написан
    Комментировать
  • Как правильно использовать в ant design GRID?

    Aetae
    @Aetae
    Тлен
    Судя по документации как-то так:
    <Row justify="space-around">
      <Col flex="none">
        ...
      </Col>
      <Col flex="none">
        ...
      </Col>
    </Row>

    Или, чтоб наверняка:
    <Row>
      <Col span={12}>
        <Row justify="center">
          <Col flex="none">
          ...
          </Col>
        </Row>
      </Col>
      <Col span={12}>
        <Row justify="center">
          <Col flex="none">
          ...
          </Col>
        </Row>
      </Col>
    </Row>


    Но, ИМХО, проще забить на эту устаревшую и ненужную надстройку и написать стили ручками: проще и гибче будет.
    Ответ написан
    Комментировать
  • Проблема с React children?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    Делая так:
    const ThemeProvider: FC = ...;
    Ты говоришь ts, что у тебя ThemeProvider имеет тип FC, и пофиг что ты там дальше пишешь. Тип FC по умолчанию не имеет children.

    Пиши либо так:
    const ThemeProvider: FC<{children: ...}> = ...;

    Либо так:
    const ThemeProvider = ({ children }: ...) => ...;
    * Кстати обрати внимание на деструктуризацию, в react component всегда приходят props, children это поле props, а не верхний аргумент.

    Либо так:
    const ThemeProvider: FC<PropsWithChildren> = ...
    * PropsWithChildren - тип помощник, добавляющий children к объекту, например
    PropsWithChildren<{
     prop2: string; 
     prop3: number
    }>
    но без дженерика просто отдаёт тип вида
    {
        children?: ReactNode | undefined;
    }
    Ответ написан
  • Как удалить теги style?

    Aetae
    @Aetae
    Тлен
    Почему ты не можешь повлиять на бэкэнд? Парсишь небось?
    Причём тут реакт? Реакт рисует элементы и стили на лету, а не получает их с бэкэннда.

    Если таки с бэка приходит html строка, которая потом вставляется через dangerouslySetInnerHTML то предварительно её почистить можно так:
    function removeStyles(html) {
      const container = document.createElement('div');
      container.innerHTML = html;
      container.querySelectorAll('style').forEach(
        style => style.remove()
      );
      return container.innerHTML;
    }


    Если ты пуляешь напрямую чужой реакт код, то тут два варианта:
    1. Делать как указал Даниил , только при этом ещё и динамически, дожидаясь пока реакт их нарисует, для чего придётся использовать MutationObserver или банальный setTimeout. Однако в таком случае стили могут успеть отрисоваться до удаления, из-за чего может происходить мерцание.
    2. Лезть глубже в исходники и патчить: подменять react функции работы со стилями, или даже тупо document.createElement, отфильтровая ненужный style. Но всё это уже требует понимания.:)
    Ответ написан
    Комментировать
  • Как исправить ошибку Module not found с версией react-scripts 5?

    Aetae
    @Aetae Куратор тега TypeScript
    Тлен
    compilerOptions.path влияет только собсно на "красные подчёркивания", tsconfig никак не управляет настоящими путями.
    Тебе надо руками прописывать alias в конфиге webpack.
    Ответ написан
    6 комментариев
  • Почему файл недоступен после сборки next js?

    Aetae
    @Aetae
    Тлен
    Я с next не работаю, но вы же сами описали проблему, что у вас _next там где должен быть build. Возможно одна из переменных окружения у вас кривая, или .env файл. Если сходу не находится откуда взялся _next - можно минимально оформатировать в ide уже собранные файлы и поискать в них это _next, окружающий код скорее всего о чём-нить скажет вам.
    В общем обычный процесс дебага...

    А вообще вот что гугл выдаёт первой ссылкой по "next js _next".
    Т.е. путь /_next/static/chunks/ - это путь по умолчанию, если нужен иной - следует задать assetPrefix.
    Почему раньше работало? А хз, может оно и так лежало в папке _next, а вы её переименовали?
    Ответ написан
    Комментировать
  • Почему не могу импортировать docx файл в реакте typescript?

    Aetae
    @Aetae
    Тлен
    Во первых где ты хочешь его импортировать? В коде или в рантайме?
    Если в коде, то чтобы что-то импортировать твой сборщик должен уметь это импортировать, и, т.к. docx явно не входит в стандартный набор форматов для front-end тебе потребуется установить и настроить специальный loader\плагин\asset в зависимости от системы сборки.
    Возможно тебе на самом деле не надо импортировать этот файл(ты с ним нечего не делаешь), а надо просто дать пользователю его скачать? Тогда просто положи его в публичную папку и используй обычную ссылку.

    Если в рантайме, а на этапе сборки его ещё нет - импорт так не работает. Используй обычный fetch.

    Если ответ не помог - потребуется больше информации: какую систему сборки используешь, кусок кода где ты пытаешься работать с файлом и т.д.
    Ответ написан
    Комментировать