2017-06-19 137 views
1

我創建了一個組件,可以讓您在按鈕上單擊添加/刪除其他下拉列表。我使用Redux來保持所選字段和值的狀態。React - 具有相同動作和縮減器的多個組件

它工作正常,但如果我在頁面上添加組件兩次(使用相同的動作和縮減器),兩個下拉列表將同時更新。

我怎樣才能讓他們獨立工作?

index.jsx

import React from 'react' 
import { connect } from 'react-redux' 
import DropDownField from './form/drop-down-field' 
import uuidV4 from 'uuid-v4' 
import { saveSelect, removeSelect, saveSelectValue } from './actions.js' 


class Component extends React.Component { 
    constructor(props) { 
    super(props); 
    } 

    saveData(e) { 
    let data = {} 
    data[e.target.name] = e.target.value 

    this.context.store.dispatch(
     addData(data) 
    ) 
    } 

    addInput = (e) => { 
    e.preventDefault() 
    this.props.saveSelect({id:uuidV4()}) 
    } 

    removeInput = (index, e) => { 
    e.preventDefault() 
    this.props.removeSelect(index) 
    } 

    saveSelectValue = (e, id) => { 
    let data = {} 
    data.id = id 
    data.value = e.target.value 

    this.props.saveSelectValue(data) 
    } 

    renderNationalitiesSelect = (selection, index) => { 
    const selectedValue = selection.value || '' 
    const id = selection.id 

    return(
     <div> 
     <DropDownField 
      key={id} 
      name={'field-'+ id} 
      value={selectedValue} 
      onChange = {(e) => { this.saveSelectValue(e, id) }} 
      required 
      options={{ 
      0: 'Please Select', 
      1: 'British', 
      2: 'French', 
      3: 'American', 
      4: 'Australian' 
     }} /> 

     <a href="#" onClick={ (e) => {this.removeInput(index, e) }}>Remove</a> 
     </div> 
    ) 
    } 

    renderCountriesSelect = (selection, index) => { 
    const selectedValue = selection.value || '' 
    const id = selection.id 

    return(
     <div> 
     <DropDownField 
      key={id} 
      name={'field-'+ id} 
      value={selectedValue} 
      onChange = {(e) => { this.saveSelectValue(e, id) }} 
      required 
      options={{ 
      0: 'Please Select', 
      1: 'United Kingdom', 
      2: 'France', 
      3: 'United States', 
      4: 'Australia' 
     }} /> 

     <a href="#" onClick={ (e) => {this.removeInput(index, e) }}>Remove</a> 
     </div> 
    ) 
    } 

    render(){ 
    const selections = this.props.selections || [] 

    let { 
     Nationality, 
     CountryOfResidence 
    } = this.props.store 

    return (
     <DropDownField name="Nationality" value={Nationality} options={{ 
     0: 'Please Select', 1: 'British', 2: 'French', 3: 'American', 4: 'Australian' 
     }} onChange={this.saveData.bind(this)} /> 

     <div> 
     <div> 
      {selections.map(this.renderNationalitiesSelect)} 
     </div> 

     {this.props.selections.length < 4 && 
      <div> 
      <a href="#" onClick={this.addInput}>Add</a> 
      </div> 
     } 
     </div> 


     <DropDownField name="CountryOfResidence" value={CountryOfResidence} options={{ 
     0: 'Please Select', 1: 'United Kingdom', 2: 'France', 3: 'United States', 4: 'Australia' 
     }} onChange={this.saveData.bind(this)} /> 

     <div> 
     <div> 
      {selections.map(this.renderCountriesSelect)} 
     </div> 

     {this.props.selections.length < 4 && 
      <div> 
      <a href="#" onClick={this.addInput}>Add</a> 
      </div> 
     } 
     </div> 

    ) 
    } 
} 

const mapStateToProps = (state) => { 
    return { 
    store: state.AddDropdown, 
    selections: state.AddDropdown.selections, 
    } 
} 

const AddDropdown = connect(mapStateToProps, {saveSelect, removeSelect, saveSelectValue})(Component) 

export default AddDropdown 

action.js

export const ADD_DATA = 'ADD_DATA' 
export const ADD_SELECT = 'ADD_SELECT' 
export const REMOVE_SELECT = 'REMOVE_SELECT' 
export const SAVE_SELECT_OPTION = 'SAVE_SELECT_OPTION' 

export function addData(data) { 
    return { type: ADD_DATA, data } 
} 

export function saveSelect(data) { 
    return { type: ADD_SELECT, data } 
} 

export function removeSelect(data) { 
    return { type: REMOVE_SELECT, data } 
} 

export function saveSelectValue(data) { 
    return { type: SAVE_SELECT_OPTION, data } 
} 

reducer.js

import ObjectAssign from 'object.assign' 
import { combineReducers } from 'redux' 
import { ADD_DATA, ADD_SELECT, REMOVE_SELECT, SAVE_SELECT_OPTION } from './actions' 

function AddDropdown(state = { selections: []}, action = {}){ 
    switch (action.type){ 
    case ADD_DATA: 
     return ObjectAssign({}, state, action.data) 
    case ADD_SELECT: 
     return { 
     ...state, 
     selections: [].concat(state.selections, action.data), 
     } 
    case REMOVE_SELECT: 
     return { 
     ...state, 
     selections: state.selections.filter((selection, index) => (index !== action.data)), 
     } 
    case SAVE_SELECT_OPTION: 
     return { 
     ...state, 
     selections: state.selections.map((selection) => selection.id === action.data.id ? action.data : selection) 
     } 
    default: 
     return state 
    } 
} 


const FormApp = combineReducers({ 
    AddDropdown 
}) 

export default FormApp 
+1

然後,你需要兩個單獨的狀態部件爲每個單獨的組件... – AJC

+0

看來你想處理可變數量的下拉,所以設置init狀態爲'state = {dropdowns:[]}'那麼你將有下拉列表(每個都有自己的選擇)。你必須改變reducer邏輯來處理多個下拉列表 – heyhugo

+0

另一個更簡單的選擇是完全跳過這個reduce,並使下拉組件有狀態。 (保持組件中的狀態) – heyhugo

回答

0

我建議isolat將每組下拉菜單作爲一個獨立的組件,然後分離每個組的縮略狀態。我的圖書館,redux-subspace是爲此目的而設計的。

index.jsx

import React from 'react' 
import { connect } from 'react-redux' 
import { SubspaceProvider } from 'redux-subspace' 
import DropDownField from './form/drop-down-field' 
import uuidV4 from 'uuid-v4' 
import { saveSelect, removeSelect, saveSelectValue } from './actions.js' 


class Component extends React.Component { 
    constructor(props) { 
    super(props); 
    } 

    saveData(e) { 
    let data = {} 
    data[e.target.name] = e.target.value 

    this.context.store.dispatch(
     addData(data) 
    ) 
    } 

    addInput = (e) => { 
    e.preventDefault() 
    this.props.saveSelect({id:uuidV4()}) 
    } 

    removeInput = (index, e) => { 
    e.preventDefault() 
    this.props.removeSelect(index) 
    } 

    saveSelectValue = (e, id) => { 
    let data = {} 
    data.id = id 
    data.value = e.target.value 

    this.props.saveSelectValue(data) 
    } 

    renderSelections = (selection, index) => { 
    const selectedValue = selection.value || '' 
    const id = selection.id 

    return(
     <div> 
     <DropDownField 
      key={id} 
      name={'field-'+ id} 
      value={selectedValue} 
      onChange = {(e) => { this.saveSelectValue(e, id) }} 
      required 
      options={this.props.options} /> 

     <a href="#" onClick={ (e) => {this.removeInput(index, e) }}>Remove</a> 
     </div> 
    ) 
    } 

    render(){ 
    return (
     <div> 
     <DropDownField name={this.props.name} value={this.props.store.value} options={this.props.options} onChange={this.saveData.bind(this)} /> 

     <div> 
      {this.props.selections.map(this.renderSelections)} 
     </div> 

     {this.props.selections.length < 4 && 
      <div> 
      <a href="#" onClick={this.addInput}>Add</a> 
      </div> 
     } 
     </div> 
    ) 
    } 
} 

const mapStateToProps = (state) => { 
    return { 
    store: state, 
    selections: state.selections, 
    } 
} 

const SingleAddDropdown = connect(mapStateToProps, {saveSelect, removeSelect, saveSelectValue})(Component) 

const AddDropdown =() => { 
    return (
     <div> 
      <SubspaceProvider mapState={state => state.nationality} namespace="nationalities"> 
       <SingleAddDropdown name="Nationality" options={{ 
        0: 'Please Select', 
        1: 'British', 
        2: 'French', 
        3: 'American', 
        4: 'Australian' 
       }}/> 
      </SubspaceProvider> 
      <SubspaceProvider mapState={state => state.countryOfResidence} namespace="countryOfResidence"> 
       <SingleAddDropdown name="Country of Residence" options={{ 
        0: 'Please Select', 
        1: 'United Kingdom', 
        2: 'France', 
        3: 'United States', 
        4: 'Australia' 
       }}/> 
      </SubspaceProvider> 
     </div> 
    ) 
} 

export default AddDropdown 

reducer.js

import ObjectAssign from 'object.assign' 
import { combineReducers } from 'redux' 
import { namespaced } from 'redux-subspace' 
import { ADD_DATA, ADD_SELECT, REMOVE_SELECT, SAVE_SELECT_OPTION } from './actions' 

function AddDropdown(state = { selections: []}, action = {}){ 
    switch (action.type){ 
    case ADD_DATA: 
     return ObjectAssign({}, state, action.data) 
    case ADD_SELECT: 
     return { 
     ...state, 
     selections: [].concat(state.selections, action.data), 
     } 
    case REMOVE_SELECT: 
     return { 
     ...state, 
     selections: state.selections.filter((selection, index) => (index !== action.data)), 
     } 
    case SAVE_SELECT_OPTION: 
     return { 
     ...state, 
     selections: state.selections.map((selection) => selection.id === action.data.id ? action.data : selection) 
     } 
    default: 
     return state 
    } 
} 


const FormApp = combineReducers({ 
    namespaced(AddDropdown, "nationality"), 
    namespaced(AddDropdown, "countryOfResidence") 
}) 

export default FormApp 

注:每my comment因爲有一些問題與此代碼,我還沒有嘗試對其進行清潔這個例子。

相關問題