Ответы пользователя по тегу JavaScript
  • Как сделать экспорт в react\redux после запроса API?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Сам вопрос поставлен вразрез с концепцией Redux, да и самой модульной концепцией JavaScript. Вам не надо пытаться экспортировать модуль после запроса, так делать нельзя. Вам надо задать начальное состояние и определить там ключи состояния isLoading, isError. При инициализации приложения произойдет запрос на сервер за данными и пока они не пришли isLoading будет иметь значение true, вы, в идеале, в этот момент должны показывать пользователю прелоадер.
    Ну, а то что вы пытались сделать это в корне не правильно.

    При использовании redux-thunk должно получится, что-то вроде:
    import { fetchInitialDataApi } from './api';
    
    const FETCH_INITIAL_DATA_REQUEST = 'FETCH_INITIAL_DATA_REQUEST';
    const FETCH_INITIAL_DATA_SUCCESS = 'FETCH_INITIAL_DATA_SUCCESS';
    const FETCH_INITIAL_DATA_ERROR = 'FETCH_INITIAL_DATA_ERROR';
    
    const fetchInitialDataRequest = () => ({ type:  FETCH_INITIAL_DATA_REQUEST });
    const fetchInitialDataSuccess = data => ({
      type:  FETCH_INITIAL_DATA_SUCCES,
      payload: data,
    });
    const fetchInitialDataError = error => ({
      type:  FETCH_INITIAL_DATA_ERROR,
      payload: error,
    });
    
    const fetchInitialData => async dispatch => {
      try {
        dispatch(fetchInitialDataRequest());
        const { data } = await fetchInitialDataApi();
        dispatch(fetchInitialDataSuccess(data));
      } catch error {
        dispatch(fetchInitialDataError(error));
      }
    };
    
    const initialState = {
      data: {},
      isLoading: false,
      isError: false,
      error: null,
    };
    
    export default function(state = initialState, action) {
      const { type, payload } = action;
    
      switch (type) {
        case FETCH_INITIAL_DATA_REQUEST:
          return {
            ...state,
            isLoading: true,
            isError: false,
            error: null,
          };
        case FETCH_INITIAL_DATA_SUCCESS:
          return {
            ...state,
            data: payload,
            isLoading: false,
          };
        case FETCH_INITIAL_DATA_ERROR:
          return {
            ...state,
            isLoading: false,
            isError: true,
            error: payload,
          };
      default:
        return state;
      }
    }


    Если же хотите получать начальные данные сразу и они статичные, то пишите их в шаблон страницы приложения в window._data и забирайте через reducer:
    export default (state = window._data) => state;
    Ответ написан
    2 комментария
  • Как заставить цикл ждать функцию?

    rockon404
    @rockon404
    Frontend Developer
    C использованием async/await можно так: https://jsfiddle.net/rockon404/ewpbd4bv/
    Ответ написан
    Комментировать
  • Есть ли разница в вызове функций?

    rockon404
    @rockon404
    Frontend Developer
    Вам уже ответили выше про подводные камни использования this в методах объектов.
    Но правильней использовать именно this. Почему? В случае необходимости изменения имени ссылки на объект или необходимости экспортировать объект как модуль, вам не придется править реализацию методов. Так же, это повышает читаемость. Ну, а в случае если метод надо передать в другое место, используйте bind:
    const a = {
      b() {
        this.с();
      },
      c() {
        alert('expected result');
      }
    }
    
    const d = {
      b: a.b.bind(a),
      c() {
        alert('wow');
      }
    };
    
    d.b(); //  => "expected result"


    Короткая форма записи методов объектов более удобна и читаема. Используйте ее в своем коде.
    Длиная форма записи:
    var obj = {
      a: function() { 
        // do something
      },
      b: function() { 
        // do something
      }
    };

    Короткая форма записи:
    const obj = {
      a() {
        // do something
      },
      b() {
        // do something
      }
    };
    Ответ написан
    Комментировать
  • Как остановит обработчик событий?

    rockon404
    @rockon404
    Frontend Developer
    var $map = $('.map');
    
    $('.icon1').click(function(event){
      $map.off('click'); 
      $map.click(function(event){ 
        console.log("from icon1");
      });
    });
    
    $('.icon2').click(function(event){ 
      $map.off('click'); 
      $map.click(function(event){ 
        console.log("from icon2");
      });
    });
    Ответ написан
    Комментировать
  • Как в webpack-dev-server можно просматривать сайт и с других устройств при разработке?

    rockon404
    @rockon404
    Frontend Developer
    добавьте в скрипт запуска сервера ключи host и port:
    "scripts": {
      "dev": "webpack-dev-server --inline --content-base dev-build/ --host xxx.xxx.xxx.xxx --port xxxx --open"
    }
    Ответ написан
    Комментировать
  • Как соединить два 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/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>
      );
    Ответ написан
    Комментировать
  • Как вызвать еще один метод при 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 комментариев
  • Как выбрать самое маленькое значение из подходящих под условие в ассоциативном массиве?

    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
    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 комментариев
  • Как выполнить массив промисов с передачей по цепочке результата?

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

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

    UPD ваш вариант тоже отлично работает.
    Видимо, у вас изначально проблема была в аргументах setTimeout.
    Ответ написан
  • Как в react подключить библиотеку?

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

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

    rockon404
    @rockon404
    Frontend Developer
    Все верно https://jsfiddle.net/yy38x0mg/
    Эта логика справедлива для любых вызовов.
    Ответ написан
    Комментировать
  • Как вернуть результат функции(метода) с отсрочкой?

    rockon404
    @rockon404
    Frontend Developer
    Странная задача, но это можно сделать так:
    anybodyName() { 
      return new Promise(resolve => {
        setTimeout(() => {
          this.anybodyProperty = 1;
          resolve(this.anybodyProperty);
        }, 1000);
      });
    }


    Использовать так:
    const someVar = this.anybodyName().then(result => {
      // do something when result be returned
    });
    Ответ написан
    8 комментариев
  • Как отловить событие onChange?

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

    rockon404
    @rockon404
    Frontend Developer
    Ответ прост:
    test.map((question, i) => question.answers = answers[i]);

    или:
    for(let i = 0; i < test.length; i++) {
      test[i].answers = answers[i];
    }
    Ответ написан
    Комментировать
  • Как разобрать короткий JS код и переделать на jQuery?

    rockon404
    @rockon404
    Frontend Developer
    $('.item-parent').append('<div class="item-decor"></div>');

    Демо: https://jsfiddle.net/sruyxars/
    Ответ написан
    Комментировать
  • Как передать функцию в родительский компонент?

    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>
    Ответ написан
  • Как исправить остановку скрипта?

    rockon404
    @rockon404
    Frontend Developer
    Иногда на функции checkResult скрипт не может получить ответ от сайта и он выполняет третье условие и перезапускает функцию checkResult снова.

    Ерунда. Нет никакого ответа. Код синхронный(не считая setTimeout) и без запросов. Проверьте свои условия. Просто выведите в теле колбека setTimeout в консоль:
    console.log(last_read);
    console.log(getRead());
    console.log(last_read < getread());
    console.log(last_read > getread());

    Наверняка там совсем не то, что вы задумали.
    А скрипт ваш не "устает", а завершает свое выполнение. Кратчайший путь для завершения: start() -> getRead() -> checkResult() -> getRead()
    Не знаю что вы задумали, но чтобы скрипт не останавливался достаточно после условий выполнять checkResult:
    function checkResult(last_read) {
            setTimeout(function() {
                if (last_read < getread()) {
                    
                } else if (last_read > getread()) {
    
                } else {
                    checkResult(last_read);
                }
    
                checkResult(getRead());
            }, 500);
        }
    Ответ написан
    2 комментария