Ответы пользователя по тегу React
  • Как в react сделать одну функцию на все элементы?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Все это можно обрабатывать в корневом компоненте.
    Ответ написан
    Комментировать
  • Почему начальное значение хуков не может быть задано пустым объектом?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    с чем это связано

    с тем же с чем и:
    let s: string;
    
    s = 182;


    Исправить можно, например, так:
    const [prod, setProd] = useState<FakeProductList | null>(null);
    
    if (!prod) {
    
    }
    
    return (/* ... */);


    Обратите внимание, что интерфейсы принято называть с большой буквы, а слово fake, пишется с буквой k.
    Атрибут readonly - лишнее.
    Ответ написан
    5 комментариев
  • Почему в React не обновляется props компонента при изменении хранилища Redux в другом компоненте?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Трудно что-то сказать, так как вы предоставили в примерах либо недостоверный, либо нерабочий код:
    componentDidMount() {
      setTimeout(() => {
        updateStore(1); // <--- WTF?
      });
    }


    Вот полностью рабочий пример кода, повторяющий логику вашего:
    import React, { useRef, useCallback } from 'react';
    import ReactDOM from 'react-dom';
    import { Provider, connect } from 'react-redux';
    import { createStore } from 'redux';
    
    const UPDATE = 'UPDATE';
    
    const update = (payload) => ({
      type: UPDATE,
      payload,
    });
    
    const reducer = (state = 0, action) => {
      if (action.type === UPDATE) {
        return action.payload;
      }
      return state;
    }
    
    const store = createStore(reducer);
    
    const mapStateToProps = state => ({ state });
    
    const  mapDispatchToProps = { update };
    
    const Header = ({ state }) => (
      <div>Header. State: {state}</div>
    );
    
    const ConnectedHeader = connect(mapStateToProps)(Header);
    
    const Body = ({ state, update }) => {
      const inputEl = useRef(null);
      const handler = useCallback(() => {
        update(inputEl.current.value);
      }, [inputEl, update]);
    
      return (
        <div>
          <div>Body. State: {state}</div>
          <input type="number" ref={inputEl} />
          <button onClick={handler}>Update state</button>
        </div>
      );
    }
    
    const ConnectedBody = connect(
      mapStateToProps,
      mapDispatchToProps,
    )(Body);
    
    const App = () => (
      <Provider store={store}>
        <ConnectedHeader />
        <ConnectedBody />
      </Provider>
    );
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);

    Демо
    Ответ написан
    Комментировать
  • Переадресация с помощью history.push не работает?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Если вы используете react-router-dom, то надо использовать именно тот экземпляр history, который использует ваш роутер.
    import React, { FC, useEffect } from 'react';
    import { RouteComponentProps, withRouter } from 'react-router-dom';
    
    interface OwnProps {
      error: string;
    }
    
    type Props = OwnProps & RouteComponentProps<any>;
    
    const HandlerErr: FC<Props> = ({ error, history }) => {
      useEffect(() => {
       const timeout = setTimeout(() => {
         history.push(`/`);
       }, 2000);
     
       return () => { 
         clearTimeout(timeout);
       };
      }, [error]);
    
      return (
        <>
          <div>{error}</div>
          <div>Contact site administrator</div>
        </>
      );
    };
    
    export default withRouter(HandlerErr);
    Ответ написан
    1 комментарий
  • Ошибка eslint в Webstorm?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    ESLint тут не причем и это не ошибка, а warning. Это плагин styled-components или анализатор, который использует этот плагин в webstorm выкидывает предупреждение, так как у вас не явно объявлен медиа запрос и он не может сопоставить его тип. Предлагаю забить.
    Ответ написан
  • Как пофиксить баг в react-router ??

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Например:
    return (
      <Switch>
        <Route exact path="/(users/.+|product/.+|search)?" component={Home} />
        {/* other routes */}
      </Switch>
    );
    Ответ написан
    5 комментариев
  • Как сделать всплывающие подсказки при вводе?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    1. Повесить слушатель на onChange, который будет делать соответствующий запрос на сервер. По приходу данные надо отрисовывать. Лучше использовать debounce и проверку минимальной длины строки перед запросом.
    2. Повесить на подсказку слушатель onClick и менять в обработчике значение нужного input.

    Простой пример реализации подобной логики:
    import React, { useState, useRef, useCallback, useMemo } from 'react';
    import debounce from 'lodash/debounce';
    import { fetchSomeData } from './someplace';
    
    const Example = () => {
      const [results, setResults] = useState([]);
      const inputEl = useRef(null);
    
      const handleTintClick = useCallback((value) => {
        inputEl.current.value = value;
      }, [inputEl]);
    
      const handleInputChange = useMemo(() => debounce(e => {
        const { value } = e.target;
    
        if (value.length < 3) return;
    
        fetchSomeData(value).then(setResults);
      }, 800), []);
    
      return (
        <>
          <input ref={inputEl} onChange={handleInputChange} />
          {results.length > 0 && (
            <ul>
              {results.map((result, i) => (
                <li
                  onClick={() => hanldeTintClick(result.title}}
                  key={result.id}
                >
                  {result.title}
                </li>
              )}
            </ul>
          )}
        </>
      );
    };

    debounce нужен чтобы оптимизировать количество запросов к серверу. Колбек будет вызван только когда пользователь сделает паузу во вводе на 800мс, если это много, то таймаут можно сделать меньше.
    useMemo вместо useCallback, чтобы не вызвать debounce каждую перерисовку.

    Понятное дело, что в реальном коде вместо ul/li должен быть стилизованный компонент. В документации MateralUI есть примеры использования их компонентов.
    Ответ написан
    Комментировать
  • Как получить ширину и высоту ref?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Как можно получить ее напрямую в render ?

    Так делать нельзя. На момент первого вызова render целевые элементы еще не смонтированы.
    Что вы вообще хотите сделать? Возможно, задачу можно решить более изящным путем.
    Ответ написан
  • Как reactjs подключается к html файлу?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Почитайте про сборщик модулей webpack и конкретно про webpackDevServer и HtmlWebpackPlugin. Вы можете выполнить npm run eject на тестовом проекте и посмотреть в файлах config/webpack.config.js, config/webpackDevServer.config.js и scripts/start.js как это все настроено и работает.

    Если вы выполнитеnpm run build и откройте директорию build/static, то увидите там все сгенерированные файлы.
    Ответ написан
    Комментировать
  • Ошибка 'Circular structure' при запуске на сервере?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Смотрите содержимое store. Вы либо пишите туда циклические структуры данных, либо пишите какой-нибудь объект сторонней библиотеки содержащий подобные данные.
    Если пишите в store собственный хелпер, использующий сторонние библиотеки, который пересоздается на клиенте, то реализуйте в нем метод toJSON:
    toJSON() {
      return '[Helper Name]';
    }
    Ответ написан
  • Как получить необязательные параметры в роуте?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Ответ написан
    Комментировать
  • Как правильно реализовать PrivateRoute ???

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Например:
    const PrivateRoute = ({ component: Component, render, isSignedIn, ...rest }) => (
      <Route
        {...rest}
        render={props => {
          if (!isSignedIn) return (
            <Redirect
              to={{
                pathname: '/login',
                state: { referrer: props.history.location.pathname },
              }}
            />
          );
    
          if (render) return render({ ...props });
    
          return <Component {...props} />;
        }}
      />
    );
    
    const mapStateToProps = (state) => ({
      isSignedIn: isSignedInSelector(state),
    });
    
    export default connect(mapStateToProps)(PrivateRoute);


    referrer тут передается для возможности возврата на целевую страницу после логина.
    Ответ написан
    3 комментария
  • Как изменить состояние одного компонента в другом?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    import React, { useState, useCallback } from 'react';
    import WeatherPage from './WeatherPage';
    import Flag from './Flag';
    
    const FlagsList = ({ onItemClick, flagsList }) => (
      <ul>
        {flagsList.map(flag => <Flag key={flag.id} flag={flag} onClick={onItemClick} />)}
      </ul>
    );
    
    const Example = () => {
      const [shouldShowWeather, setShouldShowWeather] = useState(false);
      const toggleShowWeather = useCallback(() => {
        setShouldShowWeather(state => !state);
      }, []);
    
      return (
        <>
          {shouldShowWeather && <WeatherPage />}
          <FlagsList flagsList={flagsList} onItemClick={toggleShowWeather} />
        </>
      );
    };


    Настоятельно рекомендую изучить документацию библиотеки и пройти туториал.
    Ответ написан
    Комментировать
  • Что возвращает return?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Функция передаваемая первым аргументом в setState в классовых компонентах должна возвращать объект, обновляющий какие-либо свойства объекта состояния. В вашем случае это объект содержащий свойство data. У вас в примерах, в первом случае возвращался массив, во втором был невалидный код.

    Ваш код обновления состояния можно сократить до:
    this.setState(state => ({
      data: state.data.filter(el => el.id !== id), 
    }));
    Ответ написан
    Комментировать
  • Как собирать несколько React приложений через webpack и не напортачить с относительными путями?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    file-loader + Nginx
    Ответ написан
    Комментировать
  • Почему typescript выдает ошибку при использовании generic?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Не нужны тут дженерики.
    interface Filter {
      name?: string;
      title?: string;
    }
    
    interface FiltersProps {
      filters: Filter[];
    }
    
    const Filters: React.FC<FiltersProps> = ({ filters }) => (
      <div>
        {filters.map((filter: Filter) => (
          <div key={filter.name}>
            {filter.title}
            {filter.name}
          </div>
        ))}
      </div>
    );
    
    const Main: React.FC = () =>  (
      <Filters filters={filters} />
    );
    Ответ написан
    Комментировать
  • Как сделать задержку перед отправкой запроса в react apollo?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    debounce
    Пример.
    Ответ написан
    Комментировать
  • Передать модальное окно по ссылке?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Почитайте о том, что такое dropdown select. По-хорошему вы должны использовать его.
    По поводу второго вопроса почитайте о том, что такое клиентский роутинг. Стандартом в React разработке считается react-router.
    Ответ написан
    Комментировать
  • Как обойти ошибку с типом never?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    В React разработке приветствуется подход с изменением состояния:
    interface State {
      shouldShowBtn: boolean;
    }
    
    class MyComponent extends React.Component<null, State> {
      state = {
        shouldShowBtn: true,
      };
    
      componentDidMount() {
        window.addEventListener('scroll', this.scrollHandler);
      }
    
      scrollHandler = () => {
        const { shouldShowBtn } = this.state;
    
        if (window.pageYOffset > 50 && shouldShowBtn) {
          this.setState({ shouldShowBtn: false });
        } else if (window.pageYOffset <= 50 && !shouldShowBtn) {
          this.setState({ shouldShowBtn: true });
        }
      };
    
      componentWillUnmount() {
        window.removeEventListener('scroll', this.scrollHandler);
      }
    
      render() {
        return (
          <>
           {this.state.shouldShowBtn && <button>lioih </button>}
          </>
        );
      }
    }

    или:
    const MyComponent: React.FC = () => {
      const [shouldShowBtn, setShouldShowBtn] = useState(true);
      
      const context = useMemo(() => ({ shouldShowBtn }), []);
    
      useEffect(() => {
        context.shouldShowBtn = shouldShowBtn;
      }, [shouldShowBtn]);
    
      useEffect(() => {
        const scrollHandler = () => {
          if (window.pageYOffset > 50 && context.shouldShowBtn) {
            setShouldShowBtn(false);
          } else if (window.pageYOffset <= 50 && !context.shouldShowBtn) {
            setShouldShowBtn(true);
          }
        };
    
        window.addEventListener('scroll', scrollHandler);
        
        return () => {
          window.removeEventListener('scroll', scrollHandler);
        };
      }, []);
      
      return (
        <>
         {shouldShowBtn && <button>lioih </button>}
        </>
      );
    };


    Можно еще для подобных кейсов кастомный хук написать:
    const useGetState = (initialState: any) => {
      const [state, _setState] = useState(initialState);
      const context = useMemo(() => ({ state }), []);
      const getState: any = useCallback(() => context.state, []);
      const setState = useCallback((state) => {
        context.state = state;
        _setState(state);
     }, []);
    
      return [getState, setState];
    };

    и тогда:
    const MyComponent: React.FC = () => {
      const [getShouldShowBtn, setShouldShowBtn] = useGetState(true);
    
      useEffect(() => {
        const scrollHandler = () => {
          console.log(window.pageYOffset, getShouldShowBtn());
          if (window.pageYOffset > 50 && getShouldShowBtn()) {
            setShouldShowBtn(false);
          } else if (window.pageYOffset <= 50 && !getShouldShowBtn()) {
            setShouldShowBtn(true);
          }
        }
        window.addEventListener("scroll", scrollHandler);
        return () => {
          window.removeEventListener("scroll", scrollHandler);
        };
      }, []);
      return <div>{getShouldShowBtn() && <button>lioih </button>}</div>;
    };
    Ответ написан
    2 комментария