• Почему не работает redux-saga?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    У вас ошибка тут:
    export const getAccounts = (userID) => {
      fetch(`${BASE_URL}/users/${userID}/accounts/`).then(resp => {
        resp.json().then(accounts => accounts.Items[0].accounts);
      })
    }

    Ничего не возвращаете.

    Исправить так:
    export const getAccounts = userID =>
      fetch(`${BASE_URL}/users/${userID}/accounts/`).then(resp => {
        resp.json().then(accounts => accounts.Items[0].accounts);
      });


    И лучше используйте axios, вместо fetch

    Сущность Service бесполезна в данной архитектуре, импортируйте и используйте функцию напрямую:
    response = yield call(getAccounts, userID);
    Ответ написан
    Комментировать
  • Как правильно получить данные из объекта?

    rockon404
    @rockon404
    Frontend Developer
    const { data } = response.getuserworkersinfo;
    
    const targetUser = Object.values(data).find(({ user }) => user === 'ole.5');
    
    const { rate } = targetUser;


    Демо: https://jsfiddle.net/rockon404/s4gnjffz/
    Ответ написан
    Комментировать
  • С this или без?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Потому что в первом случае вы обращаетесь к аргументу функции, а во втором к полю класса. Все по канонам JavaScript.
    Ответ написан
    Комментировать
  • Как правиль загрузить api в react?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Для асинхронных запросов следует использовать redux middleware, например redux-thunk:
    import { fetchInitialDataApi } from './api';
    
    const FETCH_DATA_REQUEST = 'FETCH_DATA_REQUEST';
    const FETCH_DATA_SUCCESS = 'FETCH_DATA_SUCCESS';
    const FETCH_DATA_ERROR = 'FETCH_DATA_ERROR';
    
    const fetchDataRequest = () => ({ type:  FETCH_DATA_REQUEST });
    
    const fetchDataSuccess = data => ({
      type:  FETCH_DATA_SUCCES,
      payload: data,
    });
    
    const fetchDataError = error => ({
      type:  FETCH_DATA_ERROR,
      payload: error,
    });
    
    const fetchData => async dispatch => {
      try {
        dispatch(fetchDataRequest());
        const { data } = await fetchDataApi();
        dispatch(fetchDataSuccess(data));
      } catch error {
        dispatch(fetchDataError(error));
      }
    };
    
    const initialState = {
      data: {},
      isLoading: false,
      isError: false,
      error: null,
    };
    
    export default function(state = initialState, action) {
      const { type, payload } = action;
    
      switch (type) {
        case FETCH_DATA_REQUEST:
          return {
            ...state,
            isLoading: true,
            isError: false,
            error: null,
          };
        case FETCH__DATA_SUCCESS:
          return {
            ...state,
            data: payload,
            isLoading: false,
          };
        case FETCH_DATA_ERROR:
          return {
            ...state,
            isLoading: false,
            isError: true,
            error: payload,
          };
      default:
        return state;
      }
    }
    Ответ написан
    4 комментария
  • Как правильно дождаться результата асинхронных функций в цепочке вызовов?

    rockon404
    @rockon404
    Frontend Developer
    Можно так:
    class Foo {
      constructor() {
        this.queue = [];
      }
    
      tick(cb) {
        this.queue.push(cb);
        return this;
      }
    
      then(cb) {
        return this.queue.reduce((acc, fn) =>
          acc.then(fn), Promise.resolve()).then(cb);
      }
    }


    Вариант в прототипном стиле:
    function Foo() {
      this.queue = [];
    }
    
    Foo.prototype.tick = function(cb) {
      this.queue.push(cb);
      return this;
    }
    
    Foo.prototype.then = function(cb) {
      return this.queue.reduce(function(acc, fn) {
        return acc.then(fn);
      }, Promise.resolve()).then(cb);
    }


    Пример использования:
    const foo = new Foo();
    
    const task1 = () =>
      new Promise(resolve => {
        setTimeout(() => {
          console.log('task1');
          resolve('Done!');
        }, 1000);
      });
    
    const task2 = arg =>
      new Promise(resolve => {
        setTimeout(() => {
          console.log('task2');
          resolve(arg);
        }, 1000);
      });
    
    foo.tick(task1)
       .tick(task2)
       .then(console.log);
    
    /* result:
    
        task1
        task2
        Done!
    
    */
    Ответ написан
    Комментировать
  • Как правильно использовать react component?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    1. Функция map превращает в колбеке элемент вашего массива данных в JSX код описывающий элемент и на выходе выдает массив таких элементов. Это лучший и общепринятый способ рендерить списки в react.
    Банальный пример:
    const employees =  ['Bill', 'Matt', 'Sarah'];
    
    return (
      <ul>
        {employees.map((employee, i) => (
          <li key={i}>employee</li>
        )}
      <ul>
    );

    Результат:
    <ul>
      <li>Bill</li>
      <li>Matt</li>
      <li>Sarah</li>
    </ul>


    2. "Element" как видите строка. Тут разработчики просто пушат в массив строку, но в идеале тут должна вызываться функция иницирующая запрос к серверу.
    Вот более приближенный к жизни пример:
    class Example extends Component {
      state = {
        elements: [],
        page: 1,
        limit: 20,
        total: 0,
        isLoading: false,
        isError: false,
      };
      
      componentDidMount() {
        this.loadThumbnails();
      }
     
      loadThumbnails = () => {
        const { page, limit } = this.state;
        
        this.setState({
          isLoading: true,
          isError: false,
        }, () => {
          axios.get(
            `api/somePath?limit=${limit}&page=${page + 1}`
          ).then(response => {
            const { data: { elements, total } } = response;
          
            this.setState(prevState => ({
              elements: [
                ...prevState.elements,
                ...elements
              ],
              page: page + 1,
              isLoading: false,
              total,
            }));
          }).catch(error => {
            this.setState({
              isLoading: false,
              isError: true,
            });
          });
        });
      };
      
      render() {
        const {
          elements,
          page,
          limit,
          total,
          isLoading,
        } = this.state;
    
        const hasMore = page * limit < total;
    
        return(
          <MasonryInfiniteScroller
            hasMore={hasMore}
            loadMore={this.loadThumbnails}
          >
            {elements.map(element => (
              <img
                key={element.id}
                src={element.thumbnail}
                style={{
                  width: element.width + 'px',
                  height: element.height + 'px',
                }}
              />
            )}
            {isLoading && <div>...Loading</div>}
            {isError && (
              <div>
                Can't load data. Please, check your internet connection.
                <button onClick={this.loadThumbnails}>
                  Try again
                </button>
              </div>
            )}
          </MasonryInfiniteScroller>
        ); 
      }
    }


    Тут как только вы пролистнете список
    Ответ написан
    1 комментарий
  • Почему не работает атрибут ref?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    У вас хандлер неправильный.
    Во-первых переименуйте с Mousedown в handleClick.
    В JavaSсript не принято называть методы с большой буквы, а хандлеры принято называть в формате handleSomeEvent или someEventHandler.

    Во-вторых либо используйте class field function:
    handleClick => e {
      console.log(this.ourDiv);
    };


    либо оберните хандлер в анонимную функцию при передаче в onClick:
    <div
      style={this.state.move}
      onClick ={() => this.handleClick()}
      ref={ourDiv => this.ourDiv = ourDiv}
    ></div>


    Проблема в том, что метод класса при передаче в другое место теряет контекст и this больше не указывает на ваш объект.
    Ответ написан
    1 комментарий
  • Как посчитать количество определенных объектов в LocalStorage?

    rockon404
    @rockon404
    Frontend Developer
    А почему не пишите массивом по одному ключу?
    Работать с массивом было бы гораздо удобней:
    var cartItems = JSON.parse(localStorage.getItem('cart'));
    var itemsQuantity = cartItems.length;
    Ответ написан
    3 комментария
  • Как избежать дублирования кода скрипта?

    rockon404
    @rockon404
    Frontend Developer
    Вам бы код научиться читать:
    $('.input-name').inputfit({ maxSize: 60 });
    $('.input-date').inputfit({ maxSize: 30 });


    Лезть в реализацию вендорных библиотек очень плохая практика. Не надо так делать. Обычно, как и в этом случае, авторы библиотек предоставляют интерфейс для ее конфигурации.
    Ответ написан
    Комментировать
  • Как сделать экспорт в 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 комментария
  • Почему у меня выводит в запросе html страницу?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Потому что у вас первый роут всегда срабатывает:
    .get('*', async ctx => {
      await ctx.render('index');
    })


    Поменяйте местами:
    .get('/api/logout', async ctx => {
      console.log('router');
      ctx.logout();
      console.log(ctx.state.user);
    })
    .get('*', async ctx => {
      await ctx.render('index');
    })
    Ответ написан
    Комментировать
  • Как заставить цикл ждать функцию?

    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
      }
    };
    Ответ написан
    Комментировать
  • Как сделать меню в Semantic UI?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    return (
      <Menu text vertical>
        <Menu.Item
          name="feed"
          active={activeItem === 'feed'}
          onClick={this.handleItemClick}
        >
          <Link to="/feed">Feed</Link>
        </Menu.Item>
      </Menu>
    );
    Ответ написан
    Комментировать
  • Как остановит обработчик событий?

    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");
      });
    });
    Ответ написан
    Комментировать
  • Как повести такую линию, от одного блока к другому?

    rockon404
    @rockon404
    Frontend Developer
    Примерно так: https://jsfiddle.net/rockon404/p5x64477/

    Вы можете менять кривизну линии. Тут такая закономерность:
    height: $value1;  
    border-radius: 50%/$value2 $value3 0 0;

    $value2 и $value3 должны быть меньше или равны $value1
    Ответ написан
    3 комментария
  • Как передать текущего user через socket io react?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    componentDidMount() {
      this.initSocket();
    }
    
    initSocket() {
    
      // set user
      socket.on('init', data => {
        this.setState({
          player: data.nickname,
        }, () => {
          alert(this.state.player);
        });
      });
    }


    1. Используйте componentDidMount, вместо componentWillMount.
    2. Никогда не меняйте state напрямую, для этого есть асинхронный метод setState. Иначе ваш компонент не обновится.
    3. Эту логику лучше вынести из компонента в redux-saga или redux-thunk.
    4. В самом компоненте пока не получили пользователя показывайте лоадер:
    render() {
      const { user } = this.state;
      
      if (!user) return <Loader />  
      
      return (
       ...
      );
    }
    Ответ написан
    1 комментарий
  • Как в 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"
    }
    Ответ написан
    Комментировать
  • Как проверить e.target элемент на наличие onClick?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Вызывайте e.stopPropagation по клику по кнопке:
    handleButtonClick = e => {
      e.stopPropagation();
      // do something else
    };

    Это остановит всплытие события.
    Ответ написан
    Комментировать
  • В чем ошибка сборки webpack?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Замените babel на babel-loader:
    module: {
        loaders: [
          {
            test: /\.jsx?$/,
            loader: 'babel-loader?presets[]=es2015,presets[]=react,presets[]=stage-0'
          },
          ... 
        ]
      }

    Ну а по-хорошему перепишите конфиг по-современному:
    module: {
        rules: [
          {
            test: /\.js$/,
            exclude: /\/node_modules\//,
            use: [
              {
              loader: 'babel-loader',
              options: {
                presets: ['es2015', 'stage-0', 'react']
              }
            }]
          },
          ...
      ]
    }
    Ответ написан