2016-11-27 3 views
0

Я использую реакцию и редукцию.Получение обновленного состояния после отправки действия в redux

У меня есть компонент, контейнер определяется как так:

import { connect } from 'react-redux'; 
import {addTag} from 'actions'; 
import ExpenseTagsControl from './expense_tags_control' 

const mapStateToProps = (state, own_props={selected_tags:[]}) => { 
    return { 
     tags_list: state.tags.tags_list 
    }; 
}; 


const mapDispatchToProps = (dispatch) => { 
    return { 
     addTag: (tag_name) => { 
      dispatch(addTag(tag_name)) 
     } 
    }; 
}; 

const AddExpenseTagsContainer = connect(
    mapStateToProps, 
    mapDispatchToProps 
)(ExpenseTagsControl); 


export default AddExpenseTagsContainer; 

Контейнера обертывание презентационного компонента, который определяется как так:

// expense_tags_control.js 
import React, {Component, PropTypes} from 'react'; 
import ChipInput from 'material-ui-chip-input'; 
import Chip from 'material-ui/Chip'; 
import Avatar from 'material-ui/Avatar'; 
import Tag from 'common/svg_icons/tag'; 
import AutoComplete from 'material-ui/AutoComplete' 

import _ from 'underscore'; 



class ExpenseTagsControl extends React.Component { 

    constructor(props) { 
     super(props); 
     this.state = { 
      chips: [] 
     }; 
    }; 

    handleAdd(chip) { 

      // If the chip does not already exist, add it. the id here will be a dummy value that is not there in the tags_list 
     if (!(_.contains(_.map(this.props.tags_list, (tag) => tag.id), chip.id))) { 
      this.props.addTag(chip.name); 
     } 

      // This is wrong.  
     this.setState({ 
      chips: [...this.state.chips, chip] 
     }); 
    }; 

    handleDelete(chip) { 
     this.setState({ 
      chips: this.state.chips.filter((c) => c !== deletedChip) 
     }); 
    }; 


    chipRenderer({ text, value, isFocused, isDisabled, handleClick, handleRequestDelete }, key) { 
     const style = { 
      margin: '8px 8px 0 0', 
      float: 'left', 
      pointerEvents: isDisabled ? 'none' : undefined 
     }; 

     return (
      <Chip key={key} style={style} onTouchTap={handleClick} onRequestDelete={handleRequestDelete}> 
       <Avatar size={24} icon={<Tag />} /> 
       {text} 
      </Chip> 
     ); 
    }; 

    render() { 
     return (
      <ChipInput 
       hintText="Tags" 
       value={this.state.chips} 
       onRequestAdd={(chip) => this.handleAdd(chip)} 
       onRequestDelete={(deletedChip) => this.handleDelete(deletedChip)} 
       fullWidth={true} 
       dataSourceConfig={{ text: 'name', value: 'id' }} 
       dataSource={this.props.tags_list} 
       chipRenderer={this.chipRenderer} 
       openOnFocus={false} 
       filter={AutoComplete.fuzzyFilter} 
       onRequestDelete={console.log("Deleted")} 
      />); 
    }; 
}; 

ExpenseTagsControl.PropTypes = { 
    tags_list: PropTypes.array.isRequired, 
    addTag: PropTypes.func.isRequired, 
    value: PropTypes.array.isRequired, 
    onChange: PropTypes.func.isRequired 
}; 

export default ExpenseTagsControl; 

Презентационный компонент выше, поддерживает состояние, которое указует чипы, которые были выбраны.

Компонент ChipInput позволяет выбирать чипы, которые являются объектами с идентификатором, и имя, определенное из ранее существовавшего источника данных. Компонент также позволяет вам добавить новый чип, введя имя. Если введенное имя не существует в источнике данных, оно добавляется к источнику данных.

Моя проблема

Идентификатор добавляемого чипа присваивается после того, как addTag() действие отправляется. Как получить значение результата только что отправленного действия?

Я думал об этом, поддерживая состояние ChipInput в глобальном состоянии и манипулируя глобальным состоянием при отправке действия addTag(). Но это слишком тяжело.

+0

Вы можете просто удалить состояние из ExpenseTagsControl и поместить его в Redux. Затем передайте его с помощью карты AddExpenseTagsContainer. – DDS

ответ

1

Если то, что я понимаю правильно, вы можете что-то вроде этого:

class ExpenseTagsControl extends React.Component { 

    // ... 


    /* 
    * assuming your reducers are working fine and 'addTag' 
    * has updated global 'state.tags.tags_list' 
    */ 
    componentWillReceiveProps(nextProps) { 
     this.setState({ chips: this.nextProps.tags_list }); 
    } 

    // ... 
} 

NB: Вам может понадобиться для оптимизации вызова setState внутри componentWillReceiveProps на основе некоторых условий, чтобы избежать ненужной повторной визуализации.

+0

tags_list - это список доступных тегов. Я передаю их этому компоненту, чтобы он мог выполнить автозаполнение. Они не являются фактическими выбранными значениями. Извините, я не был более ясен. – ashwnacharya

+0

Как я уже сказал, я принял 'dispatch (addTag (tag_name))' обновляет 'state.tags.tags_list' в каком-то редукторе. Не могли бы вы отправить код редуктора? –

1

Насколько я понимаю, проблема OP заключается в том, как отправить действие для изменения хранилища редуктов и в то же время обновить локальное состояние компонента.

Edit: добавлен рабочий пример

const initialState = { 
 
    tags: ['hello', 'hi', 'howdy'] 
 
} 
 

 
function reducer(state = {}, action) { 
 
    switch (action.type) { 
 
    case 'ADD_TAG': 
 
     return { 
 
     ...state, 
 
     tags: [ 
 
      ...state.tags, 
 
      action.payload.tag 
 
     ] 
 
     } 
 
    default: 
 
     return state; 
 
    } 
 
} 
 

 
const store = Redux.createStore(reducer, initialState); 
 

 
const addTag = (tag) => ({ 
 
    type: 'ADD_TAG', 
 
    payload: { 
 
    tag 
 
    } 
 
}) 
 

 
class Chips extends React.Component { 
 
    constructor(props) { 
 
     super(props); 
 
     this.chipToAdd = false; 
 
     this.state = { 
 
     chips: [] 
 
     } 
 
     this.handleAdd = this.handleAdd.bind(this); 
 
    } 
 

 
    componentWillReceiveProps(nextProps) { 
 
     console.log(this.chipToAdd); 
 
     if (this.chipToAdd) { 
 
     this.setState({ 
 
      chips: [...this.state.chips, this.chipToAdd] 
 
     }, (this.chipToAdd = false)); 
 
     } 
 
    } 
 

 
    handleAdd(chip) { 
 
     if (this.props.tags.filter(tag => tag === chip).length === 0) { 
 
     this.chipToAdd = chip; 
 
     this.props.addTag(chip); 
 
     } else { 
 
     if (this.state.chips.filter(existingChip => existingChip === chip).length === 0) { 
 
      this.setState({ 
 
      chips: [...this.state.chips, chip] 
 
      }); 
 
     } 
 
     } 
 
    } 
 

 
    render() { 
 
     return <div > 
 
      <h3> Tags added in component 's chip state</h3> 
 
\t \t \t <ul> 
 
\t \t \t \t {this.state.chips.map((chip, index) => <li key={index}>{chip}</li>)} 
 
\t \t \t </ul> 
 
\t \t \t <hr /> 
 
\t \t \t <h3>Tags in Redux Store</h3> 
 
\t \t \t {this.props.tags.map(
 
\t \t \t \t (tag, index) => <li key={index}> 
 
\t \t \t \t \t {tag} <button onClick={() => this.handleAdd(tag)}>Add</button> 
 
\t \t \t \t </li> 
 
\t \t \t)} 
 
\t \t \t <button onClick={() => this.handleAdd(' 
 
     new tag - ' + Math.floor((Math.random() * 100) + 1))}>Add a chip with new tag</button> 
 
\t \t </div> 
 
\t } 
 
} 
 

 
const mapStateToProps = ({ tags = [] }) => ({ tags }); 
 
const ConnectedChips = ReactRedux.connect(mapStateToProps, { addTag })(Chips); 
 

 
class App extends React.Component { 
 
\t render() { 
 
\t \t return <div> 
 
\t \t \t <h1>React/Redux Demo</h1> 
 
\t \t \t <ConnectedChips /> 
 
\t \t </div> 
 
\t } 
 
} 
 

 
const Provider = ReactRedux.Provider; 
 

 
ReactDOM.render(
 
    <Provider store={store}><App /></Provider>, 
 
    document.getElementById(' 
 
     root ') 
 
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> 
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> 
 
<script src="https://unpkg.com/[email protected]/dist/redux.min.js"></script> 
 
<script src="https://unpkg.com/[email protected]/dist/react-redux.min.js"></script> 
 
<div id="root"></div>

+0

Это не рекомендуется. Кроме того, он не работает. – DDS

+0

Заботьтесь о том, почему это не рекомендуется? Он работал, когда я тестировал его. – jpdelatorre

+0

Вы отредактировали его, так что теперь он работает. Но это, безусловно, нечеткий способ передать состояние. Если вы нажимаете достаточно быстро, вы можете перезаписать свойство chipToAdd до его использования (это полностью законно, так как React не гарантирует, что он вызовет ваши методы в любое конкретное время). Кроме того, причина, по которой React вызывает ваш компонентWillReceiveProps, даже сделать что-то не просто или просто. Это связано с React-Redux и вашим mapStateToProps, и когда это изменяется по какой-либо причине, соединение с вызовом addTag на componentWillReceiveProps может быть потеряно, что означает, что код не будет запущен, и это сломается. – DDS

Смежные вопросы