Ответы пользователя по тегу React
  • Почему React не видит class?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Так вы используете react-css-modules. У вас в код импортируются объекты, а не css.

    С react css modules БЭМ макароны писать не нужно. Достаточно писать компонентам простые и лаконичные классы вроде nav и tab, а библиотека позаботится о том, чтобы код на выходе был полностью изолирован. В этом вся суть этой библиотеки.
    Ответ написан
    Комментировать
  • Как обнаружить в чем ошибка загрузки данных с сервера (React/Redux/thunk)?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Итак, у вас отсутствуют условия инициации загрузки комментариев в CommentsList:
    componentWillReceiveProps({ isOpen, article, loadArticleComments }) {
          loadArticleComments(article.id)
      }

    каждый раз когда компонент получает новые свойства он вызывает componentWillReceiveProps. С правильно настроенными параметрами в redux он это делать будет вечно. Исправить можно так:
    componentWillReceiveProps({ isOpen, article, loadArticleComments }) {
          if (
               !article.commentsLoaded &&
               !article.commentsLoading &&
               !this.props.isOpen &&
               isOpen
             ) {
                loadArticleComments(article.id)
          }
      }

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

    В редюсере не помню что было но надо так:
    case "LOAD_ARTICLE_COMMENTS_SUCCESS": {
          return articleState
            .setIn(["entities", action.payload.articleId, "commentsLoading"], false)
            .setIn(["entities", action.payload.articleId, "commentsLoaded"], true);
        }

    По окончанию загрузки комментариев, ставим статье статусы, что комментарии загружены.

    Ну и осталось восстановить рендер в содержимого комментария в Comment и все будет работать:
    return (
        <div>
          <h5>{comment.user}</h5>
          {comment.text}
        </div>
      );
    Ответ написан
    Комментировать
  • Как работает Reactjs в связке с php?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Не надо вам изучать PHP, он вам не нужен. Не тратьте время. Пишите для учебных примеров API на Node.js. Node.js React разработчику однозначно стоит изучить.
    Попробуйте Koa или Expess простенький сервер на них написать пара минут.
    Ответ написан
    4 комментария
  • Как правильно использовать react redux при отправке формы?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Примерно так это можно сделать:

    Ваш компонент:
    import { login } from './loginDucks';
    
    class UserForm extends Component {
      ...
      handleSubmit = e => {
        this.props.login(this.state.form);
      }
      ...
      render() {
          const { isLoading, isError, shouldRedirect } = this.props;
          if (shouldRedirect) <Redirect to="/index" >;
    
        return (
          ...
        );
      }
    }
    
    const mapStateToProps = state => ({
      isLoading: state.login.isLoading,
      isError: state.login.isError,
      shouldRedirect: state.login.shouldRedirect,
    });
    
    const mapDispatchToProps = {
      login,
    };
    
    export default connect(mapStateToProps, mapDispatchToProps)(UserForm);


    loginDucks.js
    const LOGIN_REQUEST = 'LOGIN_REQUEST';
    const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
    const LOGIN_ERROR = 'LOGIN_ERROR';
    
    const loginSuccess = data => ({
      type: LOGIN_SUCCESS,
      payload: data,
    });
    
    const loginError = data => ({
      type: LOGIN_ERROR,
    });
    
    export const login = form => async dispatch => {
      try {
        const res = await axios.post('/api/user', form);
    
        if (!res.data) dispatch(loginError());
        
        dispatch(loginSuccess());
      } catch e {
        dspatch(loginError());
      }
    }
    
    const initialState = {
      isLoading: false,
      isError: false,
      shouldRedirect: false,
    };
    
    export default (state = initialState, action) => {
      const { type, payload } = action;
    
      switch(type) {
        case LOGIN_REQUEST:
          return { 
            ...state,
            isLoading: true,
            isError: false,
          };
        case LOGIN_SUCCESS:
          return { 
            ...state,
            isLoading: false,
            shouldRedirect: false,
          };
        case LOGIN_ERROR:
          return  { 
            ...state,
            isLoading: false,
            isError: true,
          };
        default:
          return state;
      }
    };


    import React from 'react';
    import ReactDOM from 'react-dom';
    import  { Provider } from 'react-redux';
    import { createStore, combineReducers, compose, applyMiddleware } from 'redux';
    import thunk from 'redux-thunk';
    import { Route, Switch } from 'react-router';
    import { BrowserRouter } from 'react-router-dom';
    import Auth from './component/AuthForm';
    import Helmet from './aplication';
    import loginReducer from './loginDucks';
    
    function user(state = {}, action){
        if(action.type === 'ADD_USER'){
            return [
                ...state,
                action.user
            ];
        }
        return state;
    }
    
    const rootReducer = combineReducers({
      login: loginReducer,
      user,
    });
    
    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
    
    const store = createStore(rootReducer, composeEnhancers(
      applyMiddleware(thunk),
    ));
    
    ReactDOM.render(
            <div>
                ...
            </div>,
        document.getElementById('content')
    Ответ написан
  • Почему у меня при перезагрузки на страницы пишет 404 ошибка?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Поправьте так:
    const Router = require('koa-router'),
        router = new Router();
    
    router
        .get('*', async ctx => {
           await ctx.render('index');
        })
    
    module.exports = router;
    Ответ написан
    Комментировать
  • Как вызвать еще один метод при success action-a, react/redux?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Так:
    export const getReq = data => (
      async dispatch => {
        try {
          dispatch({
            type: types.GET_REQUEST,
          });
    
          let res = await api.getReq(data);
          
          dispatch({
            type: types.GET_SUCCESS,
            payload: res,
          });
    
          res = await api.getReq2(data); 
    
        } catch (error) {
          dispatch({
            type: types.GET_ERROR,
          });
        }
      }
    );

    dispatch синхронный вызов, нет смысла перед ним писать await.

    Если надо запустить втрой aync action то так:
    export const getReq = data => (
      async dispatch => {
        try {
          dispatch({
            type: types.GET_REQUEST,
          });
    
          const res = await api.getReq(data);
          
          dispatch({
            type: types.GET_SUCCESS,
            payload: res,
          });
    
          dispatch(req2(res));
        } catch (error) {
          dispatch({
            type: types.GET_ERROR,
          });
        }
      }
    );
    Ответ написан
    5 комментариев
  • Что значит этот код const {date: TodayDate, transactions} = this.state?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Деструктуризация.

    Такая запись в ES6:
    const { date: TodayDate, transactions } = this.state;

    равносильна, такой записи в ES5:
    const TodayDate = this.state.date;
    const transactions = this.state.transactions;


    es6-features.org/#ObjectMatchingShorthandNotation
    Ответ написан
    Комментировать
  • Как в обработчике события получить элемент, к которому он привязан?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Используйте e.currentTarget, этот ключ объекта SyntheticEvent всегда будет указывать на элемент на котором слушается событие:
    class Example extends React.Component {
      handleClick = e => {
        alert(`
          target: ${e.target.tagName}
          currentTarget: ${e.currentTarget.tagName}
          `);
      };
      
      render() {
        return <button onClick={this.handleClick}><p>click me</p></button>;
      }
    }

    Демо: https://jsfiddle.net/ns0jutqe/
    Ответ написан
    7 комментариев
  • React Render Performance?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Можно так:
    function withLog(WrappedComponent) {
      return class extends React.Component {
        displayName = WrappedComponent.displayName || WrappedComponent.name;
        
        componentDidMount() {
          console.log(`[${this.displayName}]: componentDidMount`);
        }
    
        ...
    
        render() {
          console.log(`[${this.displayName}]: render`);
          return <WrappedComponent {...this.props} />;
        }
      }
    }
    
    class MyComp extends React.Component {
      ...
    }
    
    const MyCompWithLog = withLog(MyComp);

    Демо: https://jsfiddle.net/qg0cvjjs/
    Ответ написан
    4 комментария
  • Как работает этот AC с async/await?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Нет. В первом случае вы возвращаете из AC функцию c аргументом dispatch.
    Во втором случае вы не возвращаете ничего и убрали аргумент dispatch, заменив названием.
    А сама функция dispatch, которая передается в аргумент, как раз синхронная.

    Добавьте return и будет аналогично первому варианту:
    export const addComponent = data => {
      return async function(dispatch) {     //  тут добавлен return и аргумент
        let comps = await fire.database().ref("components");
        comps.push({
          name: data.component,
          comp: data.component_name
        });
    
         //так же в любое время вы можете вызвать dispatch
         dispatch({ type: 'someAction', payload: 'someValue' });
      };
    }

    Еще пример:
    const foo = x => y => x + y;
    аналогично:
    const foo = x => {
      return  y => {
        return x + y;
      }
    };

    Как это работает:
    const foo = x => y => x + y;
    
    const a = x(5);
    const b = a(6);
    
    console.log(b);
    // => 11

    Значение а можно представить так:
    y => 5 + y;

    Функция у вас асинхронная и пока не зарезолвится comps ее выполнение не продолжится, так как перед вызовом возвращающим значение comps стоит ключевое слово await.
    export const addComponent = data => async dispatch => {
      let comps = await fire.database().ref("components");
      // функция продолжит выполнение только когда вернется значение присвамваемое comps
      comps.push({
        name: data.component,
        comp: data.component_name
      });
    };


    Как я понимаю у вас возникли проблемы при работе с firebase?
    Могу порекомендовать использовать готовую библиотеку, вроде https://github.com/prescottprue/react-redux-firebase
    или разобраться с отличной библиотекой redux-saga и написать с помощью нее удобную реализацию взаимодействия с firebase.
    Ответ написан
    2 комментария
  • Как в react подключить библиотеку?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Выполните в директории проекта в терминале:
    npm install signalr-client -S

    В исходном коде импортируете и используете:
    import signalR from 'signalr-client';
    
    const client  = new signalR.client( ... );
    Ответ написан
    7 комментариев
  • Как в React проверить, что дочерний компонент загружен и отрисован?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Так:
    class Child extends Component {
      componentDidMount() {
        this.props.callback();
      }
    
      render() {
        return (
          <div>Child</div>
        );
      }
    }
    
    class Parent extends Component {
      onChildDidMount = () => {
        console.log('Child component was mounted!');
        // do something else
      };
    
      render() {
        return(
           <Wrapper>
             <Child callback={this.onChildDidMount} />
           </Wrapper>
        );
      }
    }
    Ответ написан
    Комментировать
  • Как отловить событие onChange?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Решения:
    1. Использовать Select плагин адаптированный под React, вместо этого. Лучшее решение, в данном случае. Да и плагинов море.
    2. Написать свой компонент-обертку для этого плагина. Костыли еще те.
    3. Написать свой Select на React. Хорошее решение, особенно для больших проектов, но требует времени.
    Ответ написан
    2 комментария
  • Как поочередно показывать элементы?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Можно, примерно, так:
    render() {
      const isUserInfoFormActive = ...; // your conditions
      const isUserAddressFormActive = ...; // your conditions
    
      return (
        <Wrapper>
          {isUserInfoFormActive && <UserInfoForm />}
          {isUserAddressFormActive && <UserAddressForm />}
        </Wrapper>
      );
    }


    Если шагов много, рациональней написать FormWidget:
    class FormWidget extends Component {
      ...
      getContent() {
         const { children, activeStep } = this.props;
    
         return React.Children.map(children, (child, i) => {
          if (activeStep === i + 1) {
            return React.cloneElement(child, { key: i });
          }
    
          return null;
        });
      }
    
      handleNext = () => {
        ...
      };
    
      handlePrev = () => {
         ...
      };
    
      render() {
        ...
        return(  
          <div>
            <Content>{this.getContent()}</Content>
            <div>
               <Button onClick={this.handlePrev}>{prevButtonLabel}</Button>
               <Button onClick={this.handleNext}>{nextButtonLabel}</Button>
            </div>
          </div>
        );
      }
    }

    и предавать детьми все шаги:
    render() {
      return (
         <FormWidget>
          <Step1 />
          <Step2 />
          <Step3 />
          <Step4 />
          <Step5 />
          <Step6 />
          <Step7 />
          <Step8 />
          <Step9 />
          <Step10 />
         </FormWidget>
      );
    }


    Пример очень абстрактный. Раскрывается сама идея.
    Всю реализацию писать, все-таки, долго.
    Ответ написан
    5 комментариев
  • Как передать функцию в родительский компонент?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Вопрос поставлен неправильно, так как вы передаете функцию в дочерний компонент, а не в родительский.

    Вам по-хорошему надо использовать arrow function и таких проблем возникать не будет:
    this.state.title.map((item, i) => (
      <TodoList
        key={i}
        index={i}
        deleteItem={descriptionArr[i]}
        title={titleArr[i]}
        deleteItem={this.deleteItem}
      />
    ))

    Тот способ, которым вы решили проблему в React сообществе использовать не принято.

    Сама проблема в том, что контекст обычной функции(написанной через ключевое слово function) ссылается в strict режиме на undefined, в обычном на window.

    Вам необходимо разобраться c основами JS и с синтаксисом ES6. Есть для этих целей хороший ресурс learn.javascript.ru
    Вашем примере видно одну семантическую ошибку, вы назвали компонент описывающий задачу TodoList, его следовало назвать TodoItem.
    То, что вы раздельно храните массивы с title и description - ужаснейшее архитектурное решение, храните задачи в виде массива объектов, описывающих сущность Todo:
    [
      {
        title: 'title',
        description: 'description',
      },
      {
        title: 'title',
        description: 'description',
      },
      {
        title: 'title',
        description: 'description',
      }
    ]
    Ответ написан
    2 комментария
  • Как выгрузить стили из объекта React?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Вы, наверно, имели ввиду массив объектов? Тогда, так:
    <Wrapper>
      {array.map(el => (
        <MyComponent key={el.id} style={{ color: el.color }}>
          {el.title}
        </MyComponent>
      ))}
    </Wrapper>
    Ответ написан
  • Как передать данные из модели в компонент React(гем React-rails)?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Ошибка тут:
    var data = this.props.data
    this.props - это свойства компонента и вызывать их можно только внутри class component - компонента созданного через расширение класса React.Component:
    class Example extends React.Component {
      render() {
        return <span>{this.props.someValue}</span>;
      }
    }


    В functional component props являются аргументом:
    function Example (props) {
      return <span>props.someValue</span>;
    }


    Вы же пытаетесь получить props из контекста глобального скоупа, который является undefined.

    Смотрите, вы пишите данные в атрибут data тега script:
    #root
    = javascript_pack_tag 'SearchUsers', {data: @users.to_json(only: [:id, :name, :email]) }

    Записываются они в виде JSON строки и их необходимо распарсить.
    Это делается так:
    import React, {Component} from 'react';
    import PropTypes from "prop-types";
    import ReactDOM from 'react-dom';
    
    let data = [];  // задаем значение по-умолчанию
    
    try {
      data = JSON.parse(document.querySelector('script[data]').getAttribute('data'));  
    } catch(e) {
      console.log(e);  // тут можно обработать ошибку парсинга данных
    }
    
    const User = ({ name, email }) => (
      <section id={name}> 
        <h1> {name} </h1>
        <h2> {email} </h2>
      </section>
    );
    
    const Users = ({ user }) => (
      <div className="users">
        {user.map(user => 
          <User key={user.id} {...user} />  // обратите внимание, заменено значение свойства key
        )}
      </div>
    );
      
    ReactDOM.render(
      <Users user={data} />,
      document.getElementById('root')
    );


    Блок try catch нужен, чтобы обработать исключение SyntaxError, если данные по какой-то причине будут записаны в тег с ошибкой. В этом случае приложение получит дефолнтное значение, которое мы присвоили при инициализации. В данном примере это пустой массив.

    Демо с простым примером без react: https://jsfiddle.net/e9q5x2ne/

    Для списка пользователей используйте для свойства key используйте user.id, вместо i.
    Ответ написан
  • Почему выдаёт ошибку при добавлении нового элемента в Array?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    state в react нельзя менять напрямую, надо использовать асинхронный метод setState.
    createCheckboxes = () => {
      const data = this.props.pets[0] || {};
      const newAnimals = Object.values(data).map(el => el.animal);
     
      this.setState(({ animals }) => ({
        animals: [ ...animals, ...newAnimals ],
      }));
    };


    Ошибка из-за того, что вы используете к колбеке map не стрелочную функцию, а обычную и в ее контексте this в strict режиме ссылается на undefined. Со стрелочной функцией такой ошибки бы не было.

    PS. Я вам советовал писать в state состояния фильтра и фильтровать массив при рендере. А вы все делаете наоборот. Неправильно используете библиотеку и функции массива. Вам надо документацию по react хорошо изучить и основы js подтянуть. Особенно возможности ES6 и экспериментальных фич используемых в react разработке.

    Советую перед тем как будете отправлять работодателю свое тестовое задание, сбросить его сюда попинать.
    Ответ написан
    4 комментария
  • Почему не обновляется props?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Мутируете старый объект состояния, а надо возвращать новый.
    Исправить можно так:
    let counterReducer = (state=initialState, action) => {
        let newstate = { ...state };
    
        switch(action.type){
            case "INC":
                newstate.counter+=action.payload
                return newstate
            case "DEC":
                newstate.counter+=action.payload
                return newstate
            default:
                return newstate
        }
    }
    Ответ написан
    4 комментария
  • Как правильно в компоненте обрабатывать события с документом?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Как-то так для скролла:
    class Header extends Component {
      state = {
        headerClass: headerClass(),
        headerSticky: headerSticky(),
      };
    
      componentDidMount() {
        window.addEventListener('scroll', this.handleScroll);
      };
    
      componentWillUnmount() {
        window.removeEventListener('scroll', this.handleScroll);
      };
    
      handleScroll = e => {
        this.setState({
          headerSticky: headerSticky(),
        });
      };
    
      render() {
        ...
      }
    }


    Для обработки ресайза лучше использовать applyContainerQuery из react-container-query:

    import { applyContainerQuery } from 'react-container-query';
    import { First, Second, Wrapper } from '../somePlace';
    
    const myQuery = {
      hasMinWidth: {
        minWidth: 1024,
      },
    };
    
    class MyComponent extends Component {
      ...
      render() {
        const {
          containerQuery: { hasMinWidth },
        } = this.props;
    
        return (
          <Wrapper>
            {hasMinWidth ? <First /> : <Second />}
          </Wrapper>
        );
      }
    }
    
    export default applyContainerQuery(MyComponent, myQuery);


    Так вы сможете обрабатывать ресайз во всем приложении.

    Вы, конечно, можете повесить слушателя как в случае со scroll, но лучше разрулить все это с помощью applyContainerQuery так как один слушатель будет менять все компоненты приложения. Ну или можете попробовать свою реализацию этой утилиты написать.
    Ответ написан
    3 комментария