2016-11-23 96 views
1

如果有用戶列表,並且每個Entry都有一個按鈕»EDIT«。如果用戶點擊它會發生以下情況:組件的每個實例的React/Redux-Individual狀態

  1. 請求的形式
  2. 服務器組件<UserEditForm />添加到進口,什麼擴展進入

這工作得很好,除了一兩件事:如果點擊其他按鈕,則表單的每個實例都會接收到最後一個請求的用戶表單的數據。那是因爲我在州內只有一個userform屬性。

因此,要解決這個問題我想換userformuserforms應該/可能是這樣的一個對象:

userforms: { 
    <id of first instance>: { … }, //formdata 
    <id of second instance>: { … }, 
    … 
} 

但因爲我是新來的反應,和/ Redux的我真的不知道該怎麼做,或者「正確」的方法或最佳實踐是真正做到這一點。

我的想法是創建一個高階組件,像這樣:

import React from 'react'; 
import {connect} from 'react-redux'; 
import {uuid} from '../../helpers/uuid'; 

export const formdatainstance = (FormInstance) => { 

    let id = null; 

    class FormDataMapper extends React.Component { 
    constructor (props) { 
     super(props); 
     id = uuid(); 
    } 

    render() { 
     //extract the formdata from the state 
     //using the id 
     return <FormInstance { ...this.props } /> 
    } 
    } 

    const mapStateToProps = (state) => { 
    console.log(id); //is null for one run 
    return { 
     userforms: state.userforms 
    }; 
    }; 

    return connect(mapStateToProps)(FormDataMapper); 
} 

所以在List組件,我可以:

import UserEditForm from './UserEditForm'; 
import {formdatainstance} from './formdatainstance'; 

const MappedUserEditForm = formdatainstance(UserEditForm); 

class List extends React.Component { 
    render(){ 
    return (
     {users.map(user => { 
     //more stuff 
     <MappedUserEditForm /> 
     //more stuff 
     })} 
    ); 
    } 
} 

所以我的問題:這是個好主意?如果是,那麼執行清理的正確方法是什麼,所以當在組件的生命週期中,我應該從狀態中刪除數據?有沒有另一種方法可以做到這一點?

感謝您的幫助!

+0

你想保持多個表單數據(每個用戶)處於狀態?或者你有一個表單數據的狀態對象就足夠了,這是由用戶填充的,並且當你點擊編輯另一個用戶時重置?你也可以發佈你的reducer的相關代碼嗎? –

+0

我想爲渲染表單的每個實例擁有唯一的表單數據。因此,如果列表中有三個用戶,我想最終以三種形式顯示每個用戶的數據,這些數據可以單獨編輯和提交。 – philipp

+0

我也很高興抽象這個功能,以便它可以應用到其他組件,它代表了系統中其他實體的另一種形式。 – philipp

回答

1

這裏是你可以做什麼......

import React from 'react'; 
import { compose } from 'redux'; 
import { connect } from 'react-redux'; 
import { reduxForm } from 'redux-form'; 

class UserEditForm extends Component { 
    ... 

    render() { 
     return <form onSubmit={this.props.handleSubmit(this.props.onSubmit)}> 
      ...form fields 
     </form> 
    } 
} 

const mapStateToProps = (state, ownProps) => { 
    return { 
     form: ownProps.formId 
    } 
} 

export default compose(
    connect(mapStateToProps), 
    reduxForm({ 
     //...other redux-form options 
    }) 
)(UserEditForm); 

你ListComponent

render() { 
    return <ul> 
     {this.props.users.map(user => 
     <li key={user.id}> 
      ... 
      <UserEditForm formId={'form-' + user.id} onSubmit={...} /> 
     </li> 
    )} 
    </ul> 
} 

這可以讓你有一個動態表單名稱。

+0

感謝您的回覆!我想我會看看redux的形式!但是你發佈的東西似乎是訣竅,會檢查它... – philipp

0

即使@jpdelatorre的答案似乎對我來說是最好的,因爲它還包含了redux-forms的鏈接,可能對我有很大幫助,我想在此發佈我的工作解決方案,只是以防有人可能覺得它有用。它只是打了我一整夜,所以需要測試我的想法是否正確,我最終能證明什麼。

我無法做到整個映射與唯一HOC,我也需要添加/修改減速器。基本上它這樣工作:

  1. 數據映射是由ID完成,

  2. 原來動作的創作者被包裹,例如,所使用的ID被附加到對象

  3. 的減速器是由»datamapped«減速包裹兩個叫

所以原來的減速器和行動創造者的代碼並不需要是陳ged,是什麼讓包裝類型易於使用。我首先想要使用即時創建的uuid,但我放棄了這一點,以便保存和恢復整個應用程序狀態。

所以HOC代碼是:

import React from 'react'; 
import {connect} from 'react-redux'; 

// The Component to wrap, 
// all of its actions 
// its default state 
export const formdatainstance = (FormInstance, Actions, defaultState = {}) => { 

    const mapStateToProps = (state) => { 
    return { 
     mappedData: state.mappedData 
    }; 
    }; 

    class FormDataMapper extends React.Component { 
    static propTypes = { 
     id: React.PropTypes.string.isRequired 
    }; 

    static contextTypes = { 
     store: React.PropTypes.object 
    }; 

    //most of mapping happens here 
    render() { 
     //wrap the action creators 
     const actions = Object.keys(Actions).reduce((list, key) =>{ 
     list[key] = (...args) => { 
      const action = Actions[key](...args); 

      //handle asyn operations as well 
      if('then' in action && typeof action['then'] == 'function') { 
      action.then(data => { 
       //attaching the id 
       this.props.dispatch({...data, id: this.props.id}); 
      }); 
      } else { 
      //attach the id 
      this.context.store.dispatch({...action, id: this.props.id }); 
      } 
     }; 
     return list; 
     }, {}), 
     //there wont be any data at first, so the default state is handed 
     //over 
     mappedProps = this.props.mappedData.hasOwnProperty(this.props.id) ? 
      this.props.mappedData[this.props.id] : defaultState; 

     //merge the hotchpotch 
     let props = Object.assign({}, mappedProps, this.props, actions); 

     //clean up 
     delete props.id; 
     delete props.mappedData; 

     return <FormInstance { ...props } /> 
    } 
    } 

    return connect(mapStateToProps)(FormDataMapper); 
}; 

減速代碼:

//hlper method 
export const createTypesToReducerMap = (types, reducer) => { 
    return Object.keys(types).reduce((map, key) => { 
    map[types[key]] = reducer; 
    return map; 
    }, {}); 
} 


export const createMappedReducer = (reducerMap, defaultState = {}) => { 
    const HANDLERS = reducerMap.reduce((handlers, typeMap) => { 
    return { ...handlers, ...typeMap }; 
    },{}); 

    return (state, action) => { 

    if (!action.hasOwnProperty('id')) { 
     if (state === undefined) return defaultState; 
     return state; 
    } 

    const reducer = HANDLERS.hasOwnProperty(action.type) ? 
      HANDLERS[action.type] : null; 

    let a = {...action}; 
    delete a.id; 


    return reducer !== null ? 
     Object.assign({}, state, { [action.id]: reducer(state[action.id], a)}) : 
     state; 
    } 
} 

終於商店:

const userEditTypeReducerMap = createTypesToReducerMap(userEditTypes, userFormReducer); 


const reducer = combineReducers({ 
    … 
    mappedData: createMappedReducer(
    [userEditTypeReducerMap], {}) 
    … 
}); 


export default compose(
    applyMiddleware(
    thunk 
) 
)(createStore)(reducer, {}); 
相關問題