• Как получать данные с сервера для компонентов?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Во-первых метод componentWillMount скоро будет deprecated. Не надо его использовать.
    Лучшее место для запроса к серверу componentDidMount, пока не пришел ответ показывайте прелоадер.
    Если используете redux то добавьте ключи isLoading и isError для каждой сущности/списка.
    Ответ написан
    Комментировать
  • Как соединить два Switch, с разными состояниями, когда при включении одного должен выключатся другой?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Cамый правильный вариант создать компонент SwitchGroup:
    const options = [
      {
        value: 'wifi',
        title: 'WiFi',
      },
      {
        value: 'isBT',
        title: 'BT',
      },
    ];
    
    class Expample extends Component {
      state = {
        switch = '',
      };
    
      handleChange = e => {
        const { name, value } = e.target;
        
        this.setState({
          [name]: value,
        });
      };
    
      render() {
        
        return (
          <Wrapper>
            <SwitchGroup
              onChange={this.handleChange}
              name="switch"
              value={this.state.switch}
              options={options}
            />
          </Wrapper>
        );
      }
    }


    Сам компонент написать просто

    class SwitchGroup extends Component {
      ...
      handleClick(value) {
         const { name, onChange } = this.props;
         ...
         onChange({ 
           e: {
             target {
               name,
               value,
             },
           }
         });
      }
    
      render() {
        return(
          <Wrapper>
            {this.props.options.map((el, i) => (
              <Switch
               key={i}
               onClick={() => handleClick(el.value)}
               isActive={el.value === this.props.value}
               ...
              >
                {el.title}
              </Switch>
            ))}
          <Wrapper />
        );
      }
    }

    Тут надо немного дописать реализацию, показана сама идея.
    Чем такое решение лучше. Оно просто масштабируется. handleChange абстрактный слушатель и может слушать все. А SwitchGroup вы сможете добавлять на страницу сколько захотите и с любым набором опций.
    Ответ написан
    5 комментариев
  • Почему React не видит class?

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

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

    rockon404
    @rockon404
    Frontend Developer
    Ловите простой пример: https://jsfiddle.net/rhc467x1/
    Ответ написан
    Комментировать
  • Как обнаружить в чем ошибка загрузки данных с сервера (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 комментариев
  • Нужно сделать 100 классов в каждом изменения на 1%, как лучше сделать?

    rockon404
    @rockon404
    Frontend Developer
    @for $i from 1 through 100 {
      .rec#{$i} {
        padding: #{$i}px;
      }
    }
    Ответ написан
    Комментировать
  • Как грамотнее делать такой лейаут на Flexbox?

    rockon404
    @rockon404
    Frontend Developer
    Лучше сделайте по аналогии с Bootstrap. Разделите сущности по обязанностям, разделите свой container на container и row.
    container ограничивает width и имеет margin: 0 auto; .
    row это flex контейнер для col. И все правильно, свойстово flex-wrap: wrap. Но тогда у колонок помимо flex: 0 0 {value}%;, определите max-width: {value}%;, чтобы сетка не сыпалась.

    В текущем варианте, ваша верстка не гибкая, так как контейнер не используешь без flex и как его сетку не используешь без ограничения ширины.

    Примерно так:
    .cotainer {
      width: $my-width-value;
      width: 100%;
      margin: 0 auto;
      padding-right: 15px;
      padding-left: 15px;
    }
    
    .row {
      display: flex;
      flex-wrap: wrap;
      margin-left: -15px;
      margin-right: -15px;
    }
    
    .col {
      flex: 0 0 $col-width:
      max-width: $col-width:
      padding-right: 15px;
      padding-left: 15px;
    }
    Ответ написан
    Комментировать
  • Что значит этот код 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
    Frontend Developer
    Отфильтровать по условиям можно так:
    var sell = [
        {"breed": "sosna", "size": "20x100x6000", "cost": 1500},
        {"breed": "sosna", "size": "30x100x6000", "cost": 2500},
        {"breed": "sosna", "size": "40x100x6000", "cost": 3500},
        {"breed": "sosna", "size": "20x100x6000", "cost": 4500},
        {"breed": "sosna", "size": "30x100x6000", "cost": 5500},
        {"breed": "sosna", "size": "40x100x6000", "cost": 6500},
    ];
    
    var breed = ["sosna"];
    var size = ["30x100x6000", "20x100x6000"];
    var params = { breed, size };
    var activeKeys = Object.keys(params).filter(function(key) {
      return params[key].length;
    });   
      
    var filtered = sell.filter(function(item) {
      return activeKeys.every(function(key) {
        return params[key].indexOf(item[key]) != -1;
      });
    });

    С использованием ES6:
    const sell = [
        {"breed": "sosna", "size": "20x100x6000", "cost": 1500},
        {"breed": "sosna", "size": "30x100x6000", "cost": 2500},
        {"breed": "sosna", "size": "40x100x6000", "cost": 3500},
        {"breed": "sosna", "size": "20x100x6000", "cost": 4500},
        {"breed": "sosna", "size": "30x100x6000", "cost": 5500},
        {"breed": "sosna", "size": "40x100x6000", "cost": 6500},
    ];
    
    const breed = ["sosna"];
    const size = ["30x100x6000", "20x100x6000"];
    const params = { breed, size };
    const activeKeys = Object.keys(params).filter(key => params[key].length);
         
    const filtered =
      sell.filter(item => activeKeys.every(key => params[key].includes(item[key])));


    Найти минимум можно так:
    var min = sell.reduce(function(acc, el) {
      return acc < el.cost ? acc : el.cost;
    }, Infinity);

    или:
    var values = sell.map(function(el) {
      return el.cost;
    });
    
    var min = Math.min.apply(Math, values);


    С использованием ES6:
    const min = sell.reduce((acc, el) => acc < el.cost ? acc : el.cost, Infinity);

    или:
    const min = Math.min(...sell.map(el => el.cost));
    Ответ написан
    8 комментариев
  • Как в обработчике события получить элемент, к которому он привязан?

    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 комментариев
  • Где установить обработчик событий?

    rockon404
    @rockon404
    Frontend Developer
    Если вы хотите описать компонент, используемый в единственном экземпляре, то лучше использовать объект, а не класс. Обработчик событий может быть как в Menu, так и внешний:

    const Menu = {
      el: document.getElementById('js-main-menu'),
      btn: document.getElementById('js-main-menu__btn'),
    
      init() {
        this.toggle = this.toggle.bind(this);
        this.close = this.close.bind(this);
        this.open = this.open.bind(this);
    
        this.btn.addEventListener('click', this.toggle);
      },
    
      toggle() {
        this.el.style.display = 
         !this.el.style.display || this.el.style.display === 'block' ? 'none' : 'block';
      },
    
      open() {
        this.el.style.display = 'block';
      },
    
      close() {
       this.el.style.display = 'none';
      }
    }
    
    export default Menu;


    import Menu from './Menu';
    
    const closeButton = document.getElementById('js-close-button');
    const openButton = document.getElementById('js-open-button');
    
    Menu.init();
    closeButton.addEventListener('click', Menu.close);
    openButton.addEventListener('click', Menu.open);


    Демо: https://jsfiddle.net/ey53xuyp/

    В случае с классом вешайте хандлеры в constructor:
    class Menu {
      constructor({ el, btn }) {
         this.el = document.getElementById(el);
         this.el = document.getElementById(btn);
    
         this.toggle = this.toggle.bind(this);
         this.close = this.close.bind(this);
         this.open = this.open.bind(this);
         
         this.btn.addEventListener('click', this.toggle);
      }  
      ...
    }
    
    export default Menu;


    import Menu from './Menu';
    
    const closeButton = document.getElementById('js-close-button');
    const openButton = document.getElementById('js-open-button');
    const menu = new Menu({
      el: 'js-main-menu',
      btn: 'js-main-menu__btn',
    });
    
    closeButton.addEventListener('click', menu.close);
    openButton.addEventListener('click', menu.open);


    Если же вы хотите использовать компонентную архитектуру, с наследованием от базового класса компонента, вам лучше использовать React или Vue. Иначе рискуете написать трудно поддерживаемых, плохо масштабируемых велосипедов.
    Ответ написан
    5 комментариев
  • Как сделать сервис создания mind map?

    rockon404
    @rockon404
    Frontend Developer
    Если бы мне приспичило реализовать подобный проект, я бы первым делом заглянул Sources сервиса, затем посмотрел подобные реализации на github.
    Вот эта библиотека https://github.com/drichard/mindmaps , нашел название в комментарии в js файле.
    На сайте по ссылке в вопросе она скопирована в чистом виде с добавлением рекламного блока.
    Посмотрите как реализована она, посмотрите на аналоги. Скорей всего вам подойдет готовое решение.
    Ответ написан
    1 комментарий
  • 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 комментария
  • Как выполнить массив промисов с передачей по цепочке результата?

    rockon404
    @rockon404
    Frontend Developer
    Так:
    this.method = function () {  
      return this.arr.reduce((acc, fn) => acc.then(fn), Promise.resolve());
    }

    Функция вернет результат выполнения цепочного вызова.
    Демо: https://jsfiddle.net/t7199xrr/

    UPD ваш вариант тоже отлично работает.
    Видимо, у вас изначально проблема была в аргументах setTimeout.
    Ответ написан
  • Как работает этот 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 комментария
  • Как выстроить блоки таким образом?

    rockon404
    @rockon404
    Frontend Developer
    Можно так: https://jsfiddle.net/geqkr0jv/
    Но height в родителях используется.
    Тут только в body, html https://jsfiddle.net/esp3cv49/
    Ответ написан
    1 комментарий