Ответы пользователя по тегу React
  • Как по клику вызвать другой компонент?

    rockon404
    @rockon404
    Frontend Developer
    class Parent extends React.Component {
      state = {
        someStateKey: false,
      };
     
      someHandler = () => {
        this.setState({ someStateKey: true });
      };
      
      render() {
        return (
          <Wrapper>
            <ChildA someHandler={this.someHandler} />
            <ChildB stateKey={this.state.someStateKey} />
          </Wrapper>
        );
     }  
    }

    Lifting state up

    Советую все таки осилить официальный Tutorial и документацию.
    Ответ написан
  • Как правильно проверить Props перед рендером компонента и сделать редирект?

    rockon404
    @rockon404
    Frontend Developer
    Метод componentWillMount не рекомендован к использованию. Давно вышел react-router 4.
    Вы можете использовать конструктор и history.push, либо render и компонент Redirect.
    Вместо PropTypes лучше использовать TypeScript или Flow.
    Ответ написан
  • Как разрулить банальную задачу по роутингу в React приложении?

    rockon404
    @rockon404
    Frontend Developer
    Компонент Router должен быть один и его обычно выносят в точку входа.
    Ответ написан
  • Почему выскакивает ошибка TypeError: scriptData is undefined?

    rockon404
    @rockon404
    Frontend Developer
    С кодом все нормально. Либо ошибка в другом месте, либо вы выполняете сборку другой версии кода.
    Ответ написан
  • Как практиковать react, redux, router?

    rockon404
    @rockon404
    Frontend Developer
    Очевидно, что с любого приложения использующего роутинг на стороне клиента:
    1. Простенький клиент для github
    2. Простенький клиент для reddit
    3. Простенький клиент для twitter
    4. Погодное приложение
    5. Небольшой дашбоард
    6. Витрина онлайн магазина
    7. Поиск авиабилетов, гостиниц и подобного
    и тд

    Вариантов очень много.
    Ответ написан
  • Прокомментируете тестовое на react?

    rockon404
    @rockon404
    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. styled components имеет встроенный инструмент injectGlobal для инъекции глобальных стилей. В библиотеке polished есть normalize:
    // styled-components usage
    injectGlobal`${normalize()}`

    Но так, как сделали вы, конечно, делать никто не запрещает.

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


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


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

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

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

    rockon404
    @rockon404
    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
    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;
    Ответ написан
  • Если функция находится в отдельном файле, срабатывает по onClick, как в нее передать евент?

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

    rockon404
    @rockon404
    Frontend Developer
    renderTab(index) {
      switch(index) {
        case 0:
          return <UserActivityJournal data={therapySessionData} />;
        /* ... */
      }
    }

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

    rockon404
    @rockon404
    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
    }
    Ответ написан
  • ООП + React + typescript как правильно создавать компоненты?

    rockon404
    @rockon404
    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);


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

    rockon404
    @rockon404
    Frontend Developer
    Почитайте любую тематическую статью на эту тему.
    1. В самом простом случае у вас две точки входа server(с ReactDom.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. Получение данных на сервере и клиенте тот еще головняк. Посмотрите как реализована библиотека frontload.
    Ответ написан
  • Не распознаёт jsx, когда добавляю package.json, как лечить?

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

    Зачем?

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

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

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

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

    rockon404
    @rockon404
    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);
    Ответ написан
  • React, как это работает?

    rockon404
    @rockon404
    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>;
    }
    Ответ написан