Ответы пользователя по тегу React
  • Как каждый раз выполнять action корневого компонента в react-router-4?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Сделайте так:

    import React, { Component } from 'react';
    import { withRouter } from 'react-router-dom';
    import { someAcion } from '../somePlace';
    
    class App extends Component {
      ...
      componentWillReciveProps(nextProps) {
        if (this.props.location !== nextProps.location) {
           this.props.someAciton();
        }
      }
      ...
    }
    
    const mapDispatchToProps = {
      someAction,
    };
    
    export default withRouter(connect(null, mapDispatchToProps)(App));
    Ответ написан
    Комментировать
  • Как в React + Redux обмениваться данными между компонентами?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    По-хорошему, надо код запроса писать вне компонента, а в компонент передавать action инициирующий запрос. Хорошим вариантом будет использовать для этого redux-saga. В Settings вы должны только запрашивать данные и как только они приходят получать их где хотите из store.

    Примерный код:
    Редьюсер и action creators:
    const FETCH_USER_DATA_REQUEST = 'FETCH_USER_DATA_REQUEST';
    const FETCH_USER_DATA_SUCCESS = 'FETCH_USER_DATA_SUCCESS';
    const FETCH_USER_DATA_FAIL = 'FETCH_USER_DATA_FAIL';
    
    export const fetchUserData = () => ({
      type: FETCH_USER_DATA_REQUEST,
    });
    
    export const fetchUserDataSuccess = data => ({
      type: FETCH_USER_DATA_SUCCESS,
      payload: data,
    });
    
    export const fetchUserDataFail = () => ({
      type: FETCH_USER_DATA_FAIL,
    });
    
    const initialState = {
      data: {},
      isLoading: true,
      isError: false,
    };
    
    export default (state = initialState, action) => {
      const { type, payload } = action;
    
      switch (action) {
        case FETCH_USER_DATA_REQUEST:
          return {
            ...state,
            isLoading: true,
          };
       
        case FETCH_USER_DATA_SUCCESS:
          return {
            ...payload,
            isLoading: false,
            isError: false,
          };
    
       case FETCH_USER_DATA_FAIL:
         return {
           ...state,
           isLoading: false,
           isError: true,
         };
       
       default: 
          return state;
      }
    };


    Сага. По вызову функции fetchUserData, тут произойдет вызов api, в случае успеха в store придут данные, в случае ошибки isError примет значение true:
    import { call, put, takeLatest } from 'redux-saga/effects';
    import {
      FETCH_USER_DATA_REQUEST,
      fetchUserDataSuccess,
      fetchUserDataFail,
    } from '../ducks/userDucks';
    import { fetchUserData } from '../api';
    
    export function* fetchUserDataSaga() {
      try {
        const { data } = yield call(fetchUserData);
    
        yield put(fetchUserDataSuccess(data));
      } catch (error) {
        yield put(fetchUserDataFail());
      }
    }
    
    export default function* watchFetchUserDataSaga() {
      yield takeLatest(FETCH_USER_DATA, fetchUserDataSaga);
    }


    Для выборки из store лучше использовать selector:
    export const userDataSelector = state => state.userData;


    Тут провайдим данные пользователя в User:
    import User from './User';
    import { userDataSelector } from '../selectors';
    
    const mapStateToProps = state => ({
      userData: userDataSelector(state),
    });
    
    export default connect(mapStateToProps)(User);


    Тут провайдим данные пользователя и fetchUserData в Settings:
    import Settings from './Settings';
    import { userDataSelector } from '../selectors';
    import { fetchUser } from '../ducks/userDucks';
    
    const mapStateToProps = state => ({
      userData: userDataSelector(state),
    });
    
    const mapDispatchToProps = {
      fetchUserData,
    };
    
    export default connect(mapStateToProps, mapDispatchToProps)(Settings);
    Ответ написан
    Комментировать
  • Как исправить предупреждение в react-router v4?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Ваша проблема решается так:
    class Login extends Component {
      componentWillMount() {
        ...
        if(isLoggedIn) {
          this.props.history.push('/main');
        }
      }
      ...
    }
    Ответ написан
  • Как правильно задеплоить приложение с базой данных?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Вы можете сейчас писать в cookie уникальный id. Утилита для генерации.
    Писать в firebase данные по этому ключу.
    При инициализации приложения проверять если cookie есть, то запросить в firebase данные по ключу. Если нет сгенерировать id и записать в cookie. В итоге заметки у всех будут разные. А подобрать чужой id ключ маловероятно.
    Правда логику обновления заметок придется изменить, но вам ее так и так придется менять.
    Тут надо будет по каждому изменению писать их массивом в firebase по одному ключу.

    А потом и с авторизацией разберетесь.

    Вместо cookie можно использовать localstorage.

    Еще проще пока писать заметки пользователей в localstorage, не используя firebase
    Ответ написан
    1 комментарий
  • Как добавить объект в массив объектов React?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    В React в большинстве кейсов важна иммутабельность и если вы получаете массив из другого места, надо так:
    const array = [{ name: 'John', age: 32}];
    
    const element = { name: 'Sally', age: 25 };
    
    const newArray = [ ...array, element ];
    
    // =>  [{ name: 'John', age: 32},  { name: 'Sally', age: 25 }]

    push можно использовать, только если вы расширяете массив в той же функции, где инициализируете.
    Ответ написан
    1 комментарий
  • Почему не могу оперировать со стандартными пропсами из store'a?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    combineReducers принимает на вход объект с функциями вида:
    (state: S, action: A) => S
    а вы передаете boolean.

    Создайте для isInputActive функцию reducer, что-то вроде
    const isInputActiveReducer = (state = false, action) => {
      const { payload, type } = action;
    
      switch(type) {
        case TOGGLE_INPUT:
          return payload;
      default:
          return state;
      }
    }


    Но вообще передавать состояние input в store плохая идея. Для этого с лихвой хватит state компонента.
    Ответ написан
    2 комментария
  • Почему при попытке вывести элементы из Firebase ничего не выходит?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Код в componentDidUpdate не верный, исправить можно так:
    componentDidMount() {
        const ideas = firebase.database().ref("items");
        let arr = [];
        ideas.on("value", snapshot => {
          let items = snapshot.val();
          for (let item in items) {
            arr.push({
              id: item,
              text: items[item].text
            });
          }
          this.props.renderIdeas(arr);
        });
      
      }

    Причина - ассинхронность. Он у вас рендерил компонент до завершения колбека on value. Так как ссылку на массив вы передали, то в консоли он отобразился и обновился, но render прошел с пустым.

    Перебирать массивы for in не рекомендуется, может быть нарушен порядок элементов. Для объектов так же лучше использовать Object.keys(obj).map
    Object.keys(obj).map(key => ({ id: key, text: obj[key].text }));


    reducer для render использовать не надо.
    Ответ написан
    2 комментария
  • Как правильно работать с роутами, полученными с сервера?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Зачем вам подгружать роуты? Вы должны добавить в приложение 6 основных роутов:
    /posts
    /comments
    /albums
    /photos
    /todos
    /users


    А конкретные сущности открывать с помощью параметров, например postId:
    <Route path="/post/:postId" component={Post}/>
    
    const Post = ({ match, postsMap }) => {
      const post = postsMap[match.params.postId];
      return (
        <div>
          <h1>{post.title}</h1>
          <p>{post.body}</p>
        </div>
    }

    Вообще для получения поста из списка постов лучше использовать selector pattern. Но это выходит за рамки темы вопроса, поэтому пример писать не буду. Но советую посмотреть библиотеку reselect.

    Сам список постов получаем GET запросом и рендерим с помощью Link:
    <List>
      {postsList.map(post => (
        <Link to={`/post/${post.id}`} key={post.id}>{post.title}</Link>
      ))}
    </List>
    Ответ написан
  • Почему AC не вызывает reducer?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Nikita Shchypylov, у тебя в коде точно что-то не правильно. Ты его сам писал или что-то копировал?
    Не хватает полных примеров кода, чтобы понять в чем причина.
    Вот что точно надо поменять:
    1. inputIdeaHandle надо передать в компонент через декоратор connect:
    import { connect } from 'react-redux';
    import { inputIdeaHandle } from '../actions';
    
    const mapDispatchToProps = {
      inputIdeaHandle,
    };
    
    export default connect(null, mapDispatchToProps)(Dashboard);

    2. changeHandle должен быть inlineFunction:
    changeHandle = e => {
      const { value } = e.target;
      this.props.inputIdeaHandle(value);
    };
    Ответ написан
    Комментировать
  • При подключении loadable-components - получаю ошибку Unexpected token?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Цитата из официальной документации:

    Use babel-plugin-syntax-dynamic-import on the client.
    Use babel-plugin-dynamic-import-node on the server.


    Вы не используете и получаете ошибку, так как в ES6 import может находиться только в верхнем скоупе и нигде более.
    Ответ написан
    1 комментарий
  • Как переписать это по нормальному???

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    function Row({ children }) {
      return <div className="descr-doc__item">{children}</div>
    }


    <Row>{`Клиент: ${client}`}</Row>
    Ответ написан
    1 комментарий
  • Как лучше сделать api call, зависящий от current state?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    1. В вашем случае сюда:
    this.setState((prevState) =>({
      currentPage : prevState.currentPage + 1 
    }), this.updateOrders);

    2. Я бы реализовал нормальный пагинатор отдельным компонентом и передавал бы в него текущую страницу, количество страниц из состояния родительского компонента и колбек getActivePage. В который бы пагинатор передавал новую страницу. Вот в колбеке уже и смотрите если страница та же то return, а выход за границы пагинации( страница меньше минимальной и больше максимальной) должен обрабатывать сам пагинатор.
    Вашу функцию showNextPage надо преобразовать в getActivePage, которая будет передаваться в пагинатор:
    getActivePage = activePage => {
      if (this.state.currentPage === activePage) return;
    
      this.setState({
        currentPage : activePage,
      }, this.updateOrders);
    };

    3. Хранить state в компоненте абсолютно нормально и надо если другие части приложения(не дочерние компоненты) ничего не должны об этом состоянии знать. Пагинация как раз тот случай. fetch лучше вынести в каталог api и передавать компоненту как свойство. Но еще лучше использовать redux + redux-saga и передавать в компонент экшен fetchOrders через mapDispatchToProps и connect. В результате в компоненте останется:
    fetchOrders = () => {
      const {
        currentPage,
        ordersPerPage,
        sortBy,
        sortDirection,
      } = this.state;
    
      this.props.fetchOrders({
        page: currentPage,
        limit: ordersPerPage,
        sortBy,
        sortDirection,
      });
    };

    Параметры запроса условные. Написал типичные для кейса пагинация + сортировка.

    Еще название updateOrders не совсем корректное, я бы назвал функцию fetchOrders.
    А инлайновые функции используйте только если они будут вызываться другим компонентом.
    Ответ написан
    Комментировать
  • Как из дочернего элемента передать id элемента по которому кликнули в React?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    По-хорошему надо хандлер передавать в кнопку, а не реализовывать в ней. Исправьте на:
    class ColumnsPanel extends Component {
      ...
      handleColumnButtonClick = e => {
        ...
      };
      ...
      render() {
       ...
       return(
         <ColumnButton
           onClick={this.handleColumnButtonClick}
         />
        ...
       )
      }
    }

    Тогда вам не придется передавать в кнопку, то о чем ей совсем не надо знать.
    Кнопка для клика и вызова переданного ей хандлера. Больше она ничего делать не должна.

    Почитайте о принципах SOLID
    Ответ написан
    Комментировать
  • Что за ошибка при попытке выполнить action?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Видимо ошибка тут:
    import {ADD_USER} from '../constans/appConstants';
    Попробуйте вывести в консоль ADD_USER, скорей всего там undefined.
    Возможно опечатка в пути импорта.
    И еще, старайтесь в actionCreator возвращать полезную нагрузку в ключе payload

    export const addUser = (name, age) => {
        return {
            type: ADD_USER,
            payload: {
                name: name,
                age: age,
            },
        }
    };
    Ответ написан
    3 комментария