Ответы пользователя по тегу Redux
  • Как можно переписать данный вариант state?

    Выглядит как будто у вас один экшен идет за несколько. Попробуйте их разбить - при этом вы можете использовать один общий action creator:

    function removeFromStructure(id) {
      return { type: REMOVE_FROM_STRUCTURE, paylodad: id }
    }
    function doSomething(id) {
      return { type: DO_SOMETHING, paylodad: id }
    }
    function commonAction(structureObject) {
      return structureObject.blockId ? removeFromStructure(id) : doSomething(id)
    }


    Код я привел просто для примера. Собственно - разбейте на несколько экшенов и обрабатывайте каждый "маленький" экшен в редьюсере.
    Ответ написан
    1 комментарий
  • Почему не меняет полностью props?

    Ваша проблема в том, что вы мутируете объект. redux, как и react, построен на иммутабельности. Я вам привел пример как нужно делать.
    Ответ написан
    Комментировать
  • Как лучше менять state в редюсер если структура это состовной объект?

    Во-первых: вы неверно обновляете структуру - ни в коем случае нельзя мутировать объект.
    Во-вторых: почему сразу не сформировать объект action с нужными названиями свойств? (_key, _value)

    const action = {
      id: 132,
      _key: 'some key',
      _value: 564
    }
    
    return {
      ...state,
      structure: {
        ...state.structure,
        [id]: { ...(state.structure[id] || {}), ...action },
      }
    }


    Выражение ниже нужно чтобы не получить ошибку, если вдруг state.structure[id] === udefined. Если вы уверены, что такого никогда не случится - можете оставить только ...state.structure[id].
    ...(state.structure[id] || {})

    UPD1. Удаление объекта

    Если вы используете lodash (или аналоги) в своем проекте, то удалить можно следующим образом:

    import _ from 'lodash'
    
    const { id, parentId } = action
    // удаляем из структуры свойство с именем id
    const nextStructure = _.omit(state.structure, id)
    return {
      ...state,
      structure: {
        ...nextStructure,
        [parentId]: {
          ...nextStructure[parentId],
          childIds: nextStructure[parentId].childIds.filter(cId => cId !== id)
        }
      }
    }


    Если же вы не используете lodash, то можно сделать так (ссылка на документацию):

    const { id, parentId } = action
    // в данном случае у нас будет переменная
    // const removed = state.structure[id]
    // а в переменную nextStructure попадут все значения, кроме id
    const { [id]: removed, ...nextStructure } = state.structure
    return {
      ...state,
      structure: {
        ...nextStructure,
        [parentId]: {
          ...nextStructure[parentId],
          childIds: nextStructure[parentId].childIds.filter(cId => cId !== id)
        }
      }
    }
    Ответ написан
    5 комментариев
  • Какая правильная структура react-redux приложения с подключением к бд mongo?

    Во-первых давайте отделим бэкенд (серверную часть): там у вас должно быть rest-api, которое возвращает JSON.

    Что касается структуры клиента: в целом - да, компоненты, контейнеры, редьюсеры (store зачем отдельно выносить?). Всю работу с api вы делаете в actions. Получается такая структура:
    - components
    - containers
    - reducers
    - actions

    Я делю приложение на модули (если оно большое). И, получается, что модули с вышеприведенной структурой находятся в отдельных папках, а само приложение это набор страниц (и их контейнеров).
    Ответ написан
  • Как правильно структурировать архитектуру приложения?

    В рамках реакта выражения "обновлять только те компоненты, у которых изменились props" - это значит "проверять props в shouldComponentUpdate". По-другому никак. даже если вы сделаете List - контейнером, внутри него тоже выполняется такая проверка.

    Но почему вы боитесь проверки props? Вся суть концепции (react+redux), что данные иммутабельны и проверка "что-то изменилось?" - это просто проверка по ссылкам (т.е. очень быстрая, никаких deepEquals).
    Команда react'а предлагает официальный add-on shallowCompare:
    shouldComponentUpdate(nextProps, nextState) {
        return shallowCompare(this, nextProps, nextState);
      }


    Я пока использую https://github.com/gaearon/react-pure-render . Делайте ваши "глупые" компоненты как PureRender и будет вам счастье.
    Ответ написан
  • Из за чего возникает предупреждение и как отследить откуда Can only update a mounted or mounting component?

    Вы бы хоть код показали. Частенько возникает, когда у вас по таймеру setState вызывается, а компонент уже удален из DOM (unmounted): перешли на другую страницу, например.
    Ответ написан
    Комментировать
  • Как получить данные через запрос?

    Сделайте экшен, который будет отвечать за подгрузку начальных данных. Сама загрузка должна происходить не в момент создания редьюсеров, а на старте приложения. Например так:

    const store = createStore(history, client, initialState)
    
    store.dispatch(appActions.loadInitialData())
    
    render((
      <Provider store={store}>
        <Router history={finalHistory}>
          {routes}
        </Router>
      </Provider>
    ), reactRootElement)


    А в loadInitialData уже загружайте данные через ajax, например:

    function loadInitialData() {
      return dispatch => axios
        .get(`${apiPrefix}/entities`)
        .then(entities => dispatch(saveEntities(entities)))
    }


    Пример очень общий, но суть, надеюсь, ясна.

    PS не забудьте thunk middleware
    Ответ написан
    Комментировать
  • Как правильно разделить ответственность между состоянием интерфейса и дерева состояния в React и REDUX?

    Лучше все это делать через редьюсеры. Флаг открытия модального окна, id айтема - все там хранить. Только в 4м пункте в модалку можно передать dropId из state, а на закрытие окна повесить общий обработчик - dropItem(id).
    Ответ написан
    1 комментарий
  • Redux actions creators внутри context. Не сожгет ли за такое инквизиция?

    Если вы их в коннекторе забиндите - а потом передадите, то все окей. Но используйте контекст когда нужно, а не везде.
    Ответ написан
  • Как осуществить связь между отдельными reducers в redux?

    Зависимостей, как правило нет. Но можно сделать вложенные редьюсеры:
    const myReducer = combineReducers({ connectedReducer1, connectedReducer2 })

    PS для форм используйте redux-form.

    UPD1 При этом вы можете вручную вызывать вложенные редьюсеры:

    function combinedReducer(state = initialState, action = {}) {
      switch(action.type) {
        case SOME_COMPLEX_ACTION:
          return {
             ...state,
             someProp: someOtherReducer(state.someProp, { type: OTHER_ACTION, payload: action.payload.something })
          }
        default:
          return {
             ...state,
             someProp: someOtherReducer(state.someProp)
          }
      }
    }
    Ответ написан
    1 комментарий
  • Как выполнить первоначальный (ре-)рендеринг в браузере React-приложения c Redux и ReactRouter?

    Попробуйте https://www.npmjs.com/package/redux-async-connect

    Эта либа как раз будет заботиться о загрузке данных при переходах по страницам + умеет в изоморфность.
    Ответ написан
    1 комментарий
  • Как правильно сделать фильтрацию списка в Redux?

    Храните тип фильтра в store а не в state. Затем фильтруйте из общего массива по этому фильтру во время mapStateToProps (изменение фильтра - это экшн). При этом не плохо бы использовать reselect.

    function mapStateToProps(state) {
      const filter = state.something.filter
      const items = state.other.items
      return {
        items: filterItems(items, filter)
      } 
    }
    connect(mapStateToProps)(...)
    Ответ написан
    4 комментария
  • Как правильно реализовать Reducer Notification?

    Каждый раз, когда мне нужно вызвать событие с уведомлением (будь то новое уведомление или удаление старого), мне нужно доставать имеющийся массив this.props.layout.notification, модифицировать его и отправлять обратно для перезаписи


    В корне не правильно. Работой со state\данными должен заниматься редьюсер. А вы ему должны говорить что и с чем сделать посредством экшенов.

    То есть если вам нужно добавить нотификейшен - диспатчите экшен вида
    dispatch({
      type: 'ADD_NOTIFICATION',
      payload: { notification: { ... } },
    })


    Естественно обернув все это в экшен-креатор.
    Ответ написан
    2 комментария
  • Как лучше организовать редьюсер в Redux?

    Насколько я понял, у вас есть две сущности (одна - root и одна в elements). Тогда это дело лучше хранить в нормализованном состоянии: делам два редьюсера, каждый работает со своей сущностью, а в селекторе денормализуем. При этом хранить сущности удобнее как { id: entity } объект, а не как массив.

    // rootEntity reducer
    const initialState = {
      entities: {}
    }
    
    function rootEntityReducer(state = initialState, action = {}) {
      switch(action.type) {
        case ADD_ELEMENT:
          const { targetEntityId, element } = action.payload
          const targetEntity = state.entities[targetEntityId]
          return {
            ...state,
            entites: {
              ...state.entities,
              [targetEntityId]: { ...targetEntity, elements: [ ...targetEntity.elements, element ] }
            }
          }
        default:
          return state
      }
    }


    UPD1 если entities - массив

    // rootEntity reducer
    const initialState = {
      entities: []
    }
    
    function rootEntityReducer(state = initialState, action = {}) {
      switch(action.type) {
        case ADD_ELEMENT:
          const { targetEntityId, element } = action.payload
    
          return {
            ...state,
            entites:  state.entities.map(
              entity => entity.id !== targetEntityId
                ? entity
                : { ...entity , elements: [ ...entity.elements, element ] }
            )
          }
        default:
          return state
      }
    }
    Ответ написан
    6 комментариев
  • Как и где хранить данные из базы в Redux приложении?

    В initialState должно быть изначальное (пустое состояние).
    Получайте данные из базы и через экшен сохраняйте в стейт.
    Ответ написан
  • Использовать Redux для передачи из ребенка к родителю значение?

    Если только у Вас не какой-то специфичный случай - редаксе все (ну или почти все) состояние храниться в хранилище, поэтому такой проблемы не возникает в принципе.

    Покажите код.

    UPD1:

    Скорее всего дело в том, что у вас стоит обработчик клика на дочернем\родительском элементе и когда выполняется:
    <div className="filter-popup" onClick={this.props.onClick}>
    ....
    closeModal() {
        this.setState({
          modalIsOpen: false
        });
        this.forceUpdate()
      },


    То событие (клик) всплывает, это в свою очередь приводит к выполнению:
    <li className="filter-result-item" onClick={this.showPopup}>
    ...
    showPopup() { ... }


    Получается, что попап скрывается и сразу же показывается вновь. Если не придираться к тому как это вообще реализовано (и т.д.) - вам нужно сделать это https://developer.mozilla.org/ru/docs/Web/API/Even...
    closeModal(e) {
        e.stopPropagation();
        this.setState({
          modalIsOpen: false
        });
      },

    Вот рабочий пример jsfiddle.net/yuvuyrhv/26
    Ответ написан
  • Нужно ли при POST запросе делать reducer в redux'е?

    Редьюсер нужен когда Вы хотите хранить состояние. Это может быть, например, статус запроса - упех/в процессе/ошибка. И исходя из этого сделать кнопку "отправить" не активной, показать индикатор загрузки и т.д.
    Ответ написан
    1 комментарий
  • Как выполнить экшен после выполнения другого екшена?

    Единственный способ нативно в редаксе выполнять экшены в определенном порядке - это thunk
    functiob action1Creator() {
      return (dispatch, getState) => {
        fetch(...)
          .then(JSON.parse)
          .then((response) => dispatch(action2Creator(response)))
        ;
      };
    }
    
    function action2Creator(response) {
      return { type: 'SOME', data: response.data[12] };
    }
    Ответ написан
    Комментировать
  • Объясните простыми словами как работает Redux?

    У вас есть одно большое дерево, в котором хранится все состояние (state) приложения - это хранилище (store).
    Также у вас есть набор редьюсеров (которые скомбинированы в один общий rootReducer) - это функции, который принимают текущее состояние и действие и возвращают новое состояние:
    function someReducer(state = initialState, action) {
      // обычно выглядит как switch 
      // action - простой js-объект
      //              и обязательно имеет строковое поле type
      switch(action.type) {
        // обрабатываем действие с типом SOME_ACTION_NAME
        case 'SOME_ACTION_NAME':
          // берем какие-то данные из экшена и возвращаем новое состояние
          // при этом менять sate нельзя!
          // state.someProperty = action.newStateData <--- НЕТ!
          return { ...state, action.newStateData };
        // Если мы не обрабатываем действие - просто возвращаем старое состояние
        default:
          return state;
      }
    }


    Также есть экшен креаторы (actionCreators) - это функции, которые возвращают действие. затем это действие вещается в хранилище (диспатчится). Типичный пример:
    function someActionCreator(someArg) {
      return {
        type: 'SOME_ACTION_NAME',
        newStateData: someArg + 5, // <-- разная логика
      };
    }


    По-умолчанию в качестве экшена мы можем вернуть только простой объект, но при создании хранилища можно добавить так называемый middleWare. Это специальные функции, которые принимают все экшены из диспатча и могут передавать их дальше (при этом содержат дополнительную логику).

    Если мы хотим получить доступ к состоянию в экшен креаторе - воспользуемся thunkMiddleware:
    import thunkMiddleware from 'redux-thunk';
    
    function createStore(initialState) {
      const reducer = combineReducers(reducers);
      const finalCreateStore = applyMiddleware(
        thunkMiddleware // <-- добавляем middleware
      )(defaultCreateStore);
      return finalCreateStore(reducer, initialState);
    }


    Теперь мы можем делать так:
    function someActionCreator(someArg) {
      return (dispatch, getState) => { // <-- возвращаем фукнцию, а не объект!
        const someState = getState().reducerName;
        return {
          type: 'SOME_ACTION_NAME',
          newStateData: someArg + someState, 
        };
      };
    }


    В общем схема выглядит так:

    actionCreator --action--> dispatch --action--> middleware --action--> store --action--> reducer --> newState


    Затем мы берем из react-redux метод connect, который подключает Ваш умный компонент к хранилищу:
    import { connect } from 'react-redux';
    import { bindActionCreators } from 'redux';
    
    class MyComponent extends Component {
      static propTypes = {
        someProp: PropTypes.string.isRequired,
        someFunc: PropTypes.func.isRequired,
      };
    }
    
    // Тут мы берем из глобального состояния необходимую нам часть
    // В ownProps - свойства компонента. Тут могут быть например свойства от роутера
    function mapStateToProps(state, ownProps) {
      return {
        someProp: state.someReducer,
      };
    }
    
    function mapActionsToProps(dispatch) {
      return bindActionCreators ({ // <-- биндим все на disptach для удобства
        someFunc: (someArg) => someActionCreator(someArg + 1),
      }, dispatch);
    }
    
    export default connect(
      mapStateToProps,
      mapActionsToProps
    )(MyComponent);
    Ответ написан
    3 комментария