Ответы пользователя по тегу React
  • Прокомментируете тестовое на react?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    1. Хотелось бы видеть в проекте использование redux. react+redux - это самый распространенный и востребованный стек в React разработке.

    2. Почему все хандлеры и состояния находятся в App, а не в Main? Как вы потом эту кашу собираетесь масштабировать? Переносите все, что связанно только с Main в Main. По-хорошему смотрите пункт 1.

    3. Слишком много функциональных компонентов. Подумайте их где можно заменить на классы с реализованным shouldComponentUpdate или на PureComponent, чтобы убрать лишние вызовы render этих компонентов.

    4. import Logo from 'images/Logo.png';
    называть пути к ресурсам с заглавной буквы неправильно.

    5. Вместо:
    const StyledLogo = styled.img.attrs({
      src: Logo,
      alt: 'Aviasales'
    })`
      width: 60px;
      height: 61px;
    `;

    Удобней в использовании:
    const StyledLogo = styled.img`
      width: 60px;
      height: 61px;
    `;

    и:
    <StyledLogo src={logo} alt="Aviasales" />

    6.
    const Error = ({ text }) => (
      <StyledError dangerouslySetInnerHTML={{__html: text}} />
    );

    зачем тут html?
    Для сохранения переносов строки есть css правило:
    white-space: pre-line;

    7. Вместо:
    let element;
    
    if (error && !isLoading) {
      element = <Error text={error} />;
    }
    if (!error && isLoading) {
      element = <Loader />;
    }
    if (!error && !isLoading) {
      element = (
        <>
        <Heading />
        <Main
        isCurrencyExchanging={isCurrencyExchanging}
        activeCurrency={activeCurrency}
        handleCurrencyChange={this.handleCurrencyChange}
        ticketsFilteredByStops={ticketsFilteredByStops}
        stops={stops}
        handleStopsChange={this.handleStopsChange}
        handleUncheckOther={this.handleUncheckOther}
        />
        </>
      );
    }
    return element;

    Лучше:
    if (isLoading) return <Loader />;
    
    if (error) return <Error text={error} />;
    
    return (
      <>
        <Heading />
        <Main
          isCurrencyExchanging={isCurrencyExchanging}
          activeCurrency={activeCurrency}
          handleCurrencyChange={this.handleCurrencyChange}
          ticketsFilteredByStops={ticketsFilteredByStops}
          stops={stops}
          handleStopsChange={this.handleStopsChange}
          handleUncheckOther={this.handleUncheckOther}
        />
      </>
    );


    8. Вместо:
    filterTickets = (tickets, stops) => {
      return tickets.filter((ticket) => {
        return values(stops).indexOf(ticket.stops) !== -1;
      });
    };

    Лучше:
    filterTickets = (tickets, stops) => tickets.filter(
      ticket => values(stops).includes(ticket.stops),
    );


    9. Не пропускайте отступы между методами и между вложенными свойствами css.

    10. Вместо:
    componentsDidMount() {
      // много кода
    }


    Лучше:
    componentsDidMount() {
      this.fetchSomeData();
    }


    11. Директории и индексные файлы для каждого компонента, имхо, лишнее. Лучше компоненты определять в одноименном файле и только когда возникнет необходимость в его декомпозиции, заменять на директорию и index.

    12. Loader и Error самое место в директории components/core или что-то вроде того. Там же, по-хорошему, должны находиться базовые компоненты: кнопки, инпуты, табы, чекбоксы.

    13. Styled компоненты, имхо, лучше писать в файле с компонентом, где они применяются. Так анализ кода происходит гораздо быстрей и легче поддерживать. Исключение - переиспользуемые компоненты.
    Даже если вам больше нравится выносить, называть файл style неправильно, вы там описываете компоненты, а не просто стили.
    Ответ написан
    3 комментария
  • Как запретить Jest'y проходить по import'ам??

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    ы в файле User импортируете formatTime из индексного файла. Так делать не надо. Тут для легкого тестирования либо props передавать, либо DI для тестирования, либо просто импортируйте не с индексного файла.
    Замените:
    import { formatTime } from "../../../utils";
    на:
    import { formatTime } from "../../../utils/chatFormatTime";


    А все, что связанно с formatTime из файла теста удалите. Ну и моки props для user нужны.

    И еще избегайте циклических зависимостей. В папку utils не должен попадать код из других директорий, только из node_modules и конфигурационных файлов. Если у вас через конфиг в utils попадает store с вашим кодом, что-то не так.
    Ответ написан
  • Как навязать двустороннюю связь между компонентами в ReactJS?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    1. Объявлять componentDidMount стрелочной функцией - маразм. В этом нет никакого смысла.

    2. Если ваша функция выполняется синхронно достаточно сократить до:
    const response = myFunction(1);
    this.setState({ response });


    3. Если асинхронно:
    componentDidMount() {
      const { someValue } = this.props;
      // асинхронный вызов, возвращающий Promise
      fetchSomeData(someValue).then(response => this.setState({ response }));
    }

    или:
    async componentDidMount() {
      const { someValue } = this.props;
      // асинхронный вызов, возвращающий Promise
      const response = await fetchSomeData(someValue);
    
      this.setState({ response }));
    }


    4. Сама функция myFunction написана очень плохо содержит ошибку. Лучше:
    const myFunction = num => ++num;
    Ответ написан
    5 комментариев
  • Если функция находится в отдельном файле, срабатывает по onClick, как в нее передать евент?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Он и так передается первым аргументом при вызове. Просто используйте его в своем хандлере:
    handleCountyItemClick = e => {
      // do something with e
    };
    Ответ написан
  • Где делать конвертацию?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    В редьюсере.
    Ответ написан
    Комментировать
  • Как можно упростить рендер компонента?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    renderTab(index) {
      switch(index) {
        case 0:
          return <UserActivityJournal data={therapySessionData} />;
        /* ... */
      }
    }

    Так вы избежите кучи ненужных проверок, каждый render.
    Ответ написан
    Комментировать
  • Как через ref определить в фокусе элемент или нет?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    const isFocused = document.activeElement === this.inputRef.current;
    Ответ написан
    Комментировать
  • Нормально ли написаны компоненты??

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    1. formatTime - хелпер, я не буду таратить время на анализ качества его реализации, скажу лишь, что в компоненте ему не место. Перенесите в папку lib, utils или как там она у вас называется, импортируйте где нужен и используйте.

    2. Закроем, пока глаза на пункт 1. formatTime у вас написан стрелочной функцией, но он никуда не передается и в нем не используется ключевое слово this - определять его стрелочной функцией было бессмысленно. Изучите вопрос и постарайтесь понять зачем их используют.
    spoiler
    привязка контекста

    То же с getChatsUser - функция никуда не передается.

    3. Никогда не используйте тернарки если, альтернативный кейс null. Замените на:
    render() {
      /* ... */
      const hasUnreadMessages = unread_amount > 0;
      
      return (
        <div>
          {hasUnreadMessages && (
            <div className="unread-message-container">{unread_amount}</div>
          )}
        </div>
      );
    }


    4. Зачем передаете в mapStateToProps все состояние редьюсера? Передавайте только необходимые компоненту данные:
    export const userDataSelector = state => state.chatReducer.userData;

    const mapStateToProps = state => ({
      userData: userDataSelector(state),
    });

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

    Почитайте про библиотеку reselect.

    5. Вместо:
    const query = Object.assign({}, params, { per_page });

    лаконичней и проще для анализа:
    const query = { ...params, per_page };

    6. Правильное обновление состояния на основе предыдущего:
    this.setState(prevState => ({
      per_page: prevState.per_page + 5,
    }));


    7. Использование trailing comma - очень хорошая практика, советую ей не пренебрегать.

    8. Более оптимизированным вариантом для списков, будет вместо определения стрелочных функции в элементах каждый render:
    <div onClick={() => setUserForChat(userData)}>
    использовать data-атрибуты:
    <div data-id={userData.id} onClick={setUserForChat}>

    Реализацию setUserForChat придется изменить:
    setUserForChat = e => {
      const { id } = e.target.dataset;
      // some code with using id
    }
    Ответ написан
    2 комментария
  • ООП + React + typescript как правильно создавать компоненты?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Я так пишу:
    import * as React from 'react';
    import { connect, DispatchProp } from 'react-redux';
    import { connectedPropSelector } from './selectors';
    
    interface OwnProps {
      ownProp: string,
    }
    
    interface ConnectedProps {
      connectedProp: string,
    }
    
    type Props = OwnProps & ConnectedProps & DispatchProp<any>;
    
    interface State {
      someKey: string,
    }
    
    class Example extends React.Component<Props, State> {
      state = {
        someKey: 'someValue',
      };
      
      render() {
        const { ownProp, connectedProp } = this.props;
    
        return ( /* ... */ );
      }
    }
    
    const mapStateToProps = state => ({
      connectedProp: connectedPropSelector(state),
    });
    
    export default connect(mapStateToProps)(Example);


    Официальная документация
    Ответ написан
    1 комментарий
  • Что такое изоморфное приложение и как его создать?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Почитайте любую тематическую статью на эту тему.
    1. В самом простом случае у вас две точки входа server(с ReactDOMServer.renderToString) и client(c ReactDOM.hydrate).

    2. State можно получать на сервере и передавать на клиент, примерно, так:
    <script>window.__INITIAL_STATE__ = json.stringify(initialState);</ script>

    В configureStore:
    export = function (initialState: {} = {}) {
      if (__CLIENT__) {
        initialState = JSON.parse(JSON.stringify(window.__INITIAL_STATE__));
      }
    
      return createStore(combineReducers({ ...rootReducer }), initialState, enhancer);
    }

    3. Никаких глобальных переменных для состояний изменяющихся от клиента к клиенту(локали, темы и прочее). Node обрабатывает множество запросов одновременно и информация клиентов при передаче конфигураций в глобальные модули или при использовании глобальных состояний может перемешаться.
    4. Получение данных на сервере и клиенте тот еще головняк. Посмотрите как реализована библиотека react-frontload.
    Ответ написан
    Комментировать
  • Не распознаёт jsx, когда добавляю package.json, как лечить?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    ...но если кладу этот файл в отдельную папку и рядом с файлом создаю файл package.json с таким содержанием...

    Зачем?

    Срочно изучайте основы npm и wepack.

    Если хотите собирать бандл из другой директории измените путь в свойстве entry конфига webpack:
    module.exports = {
      entry: './path/to/my/entry/file.js'
    };

    package.json добавлять не надо, этот файл для других целей.
    Ответ написан
    2 комментария
  • Почему render выполняеться перед componentWillMount?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Потому что библиотека не ожидает возвращаемое значение в componentWillMount и следом сразу вызывает render.
    Забудьте этот метод - он не рекомендован к использованию. Как уже писали выше, асинхронные запросы рекомендуется инициировать в componentDidMount. Он выполняется после вызова render, который вы по какой-то неясной причине хотите избежать.

    Вот два варианта решения:
    1. До загрузки данных возвращать в render null или прелоадер.
    render() {
       const { data } = this.props;
    
       if (!data) return null;
         
        return (
          <div>
            <FilterModels />
          </div>
        )
      }
    }

    2. Переместить загрузку данных в родительский компонент и отрисовывать целевой по условию:
    render() {
      const { filterData } = this.props;
      return (
        <Wrapper>
          {filterData  && <FilterForm data={filterData} />}
       </Wrapper>
      )
    }
    Ответ написан
    Комментировать
  • Как правильно организовать общение между компонентами?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Можно сделать так:
    1. Метод оnClick передается из родительского компонента.
    2. state иконки высчитывается по полученным props, но так же меняется по клику, для более комфортного пользовательского опыта.
    3. По возвращению ответа от сервера обновляется вся карточка. Так же, обновляется state иконки в computeDerivedState, мало ли была ошибка и товар не попал в избранное.
    4. В случае ошибки, левом нижнем углу экрана можно показать всплывающее уведомление. Для этого можно написать action и вызывать его в блоке catch асинхронных действий.
    Ответ написан
  • Как сделать глобальную переменную в REACT?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Хорошим вариантом будет передавать переменные зависимые от окружения в webpack.DefinePlugin:
    new webpack.DefinePlugin({
      '__HOST__': JSON.stringify('https://example.com')
    });

    В коде:
    xhr.open("GET", __HOST__ + `/api/v1.0/items/${first}/${this.state.itemPerPage}`, true);
    Ответ написан
    4 комментария
  • React, как это работает?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Запись:
    state = {
      filteredNews: this.props.data,
    };

    аналогична:
    constructor(props) {
        super(props);
        this.state =  {
          filteredNews: this.props.data,
        };
      }

    this.props.data записывается в state единожды в конструкторе. В последствии, при получении нового значения this.props.data, state не изменяется.

    Нет никакой необходимости определять renderNews как стрелочную функцию, ее следует переделать в метод класса.

    Было:
    renderNews = () => {};
    стало:
    renderNews() {}

    Я бы внес в renderNews следующие изменения, помимо переделывания в метод класса:
    1. ранний return вместо переменной и блока else,
    2. замена обычной функции на стрелочную в колбеке map.

    Результат:
    renderNews() {
      const { filteredNews } = this.state;
    
      if (filteredNews.length) {
        return filteredNews.map(item => (
          <Article key={item.id} data={item} />
        ));
      }
    
      return <p>К сожалению новостей нет</p>;
    }
    Ответ написан
    4 комментария
  • Как отрендерить компоненту react только после ajax запроса, который обновит store?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    render() {
      const { someStateKey } = this.props;  
    
      if (!someStateKey) return null;
    
      return (
        {/* component JSX code */}
      );
    }
    Ответ написан
    2 комментария
  • Для создания приложения на React, нужно ли сначала целиком делать верстку приложения?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Нет, это будет двойная работа. В реальном проекте программист:
    1. Описывает компонент
    2. Пишет JSX разметку в render
    3. Стилизует компонент, в зависимости от выбранной технологии
    4. Пишет необходимые методы и слушатели
    Ответ написан
    1 комментарий
  • Можно ли после основ JS перейти к изучению React?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Для того, чтобы начать изучение, возможно, хватит. Подтяните ES6 и промисы.
    Для того, чтобы написать полноценное, поддерживаемое SPA основ JS и изучения React в кратчайшие сроки, скорей всего не хватит.

    Я так же, в свое время, JavaScript параллельно с Backbone изучал. Надо было в кратчайшие сроки начать писать приложение и меня выдернули из мобильной разработки. Так вышло, что навсегда.)
    Ответ написан
    Комментировать
  • Как сделать исчезающую/появляющуюся стрелочку "вверх" на React?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    class ArrowUp extends Component {
      state = {
        isVisible: false,
      };
    
      componentDidMount() {
        window.addEventListener('scroll', this.handleScroll);
      }
    
      componentWillUnmount() {
        window.removeEventListener('scroll', this.handleScroll);
      }
    
      handleScroll = () => {
        if (window.scrollY >= SOME_VALUE  && !this.state.isVisible) {
          this.setState({ isVisible: true });
        } else if (window.scrollY < SOME_VALUE && this.state.isVisible) {
          this.setState({ isVisible: false });
        }
      };
    
      render() { /* ... */ }
    }
    Ответ написан
    Комментировать
  • Как сделать простую прокрутку (без анимации) до якоря без изменения адресной строки (без jQuery на React)?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Проще некуда:
    element.scrollIntoView();
    Демо

    В имплементации React как-то так:
    handleLinkClick = e => {
      e.preventDefault();
      const element = document.getElementById(e.target.href);
      element.scrollIntoView();
    };
    Ответ написан
    Комментировать