2017-06-06 35 views
0

我寫了一個泛型表組件,使用兩個不同的數據集創建兩次。我使用Redux商店來保護道具,當我點擊它時嘗試刪除一行。React + Redux:reducer將不會重新加載組件

組件顯示良好,onClick函數被觸發並調用reducer。新的返回狀態返回之前的狀態,但沒有單擊元素。

我的問題是,組件不重新呈現。 根據故障排除頁面(http://redux.js.org/docs/Troubleshooting.html),似乎我不改變以前的狀態(也許我只是不明白該怎麼做),我打電話dispatch()函數,我的mapStateToProps似乎是正確的因爲數據在頁面加載時很好地顯示。我嘗試在reducer.js>表函數中定義初始狀態,而不是使用createStore函數來執行它,我試圖將'表'reducer拆分爲'domains'和'hosts'reducer(重複代碼),我嘗試了不使用combineReducers(),因爲我只有一個(但我將來會有更多),我嘗試了很多東西,所以我不記得了。

我敢打賭,這不是什麼大不了的事,但我無法弄清楚發生了什麼。你能幫我嗎 ?非常感謝你。


reducer.js

import { combineReducers } from 'redux' 
import { DELETE_DOMAIN, DELETE_HOST } from './actions' 

function deleteItem(state, index) { 
    let newState = Object.assign({}, state) 

    newState.items.splice(index, 1) 

    return newState 
} 

function tables(state = {}, action) { 
    switch (action.type) { 
     case DELETE_HOST: 
      return Object.assign({}, state, { 
       hosts: deleteItem(state.hosts, action.host) 
      }) 

     case DELETE_DOMAIN: 
      return Object.assign({}, state, { 
       domains: deleteItem(state.domains, action.domain) 
      }) 

     default: 
      return state 
    } 
} 

const reducers = combineReducers({ 
    tables, 
}) 

export default reducers 

actions.js

export const DELETE_DOMAIN = 'DELETE_DOMAIN' 
export const DELETE_HOST = 'DELETE_HOST' 

export function deleteDomain(domain) { 
    return { type: DELETE_DOMAIN, domain } 
} 

export function deleteHost(host) { 
    return { type: DELETE_HOST, host } 
} 

index.js

import React from 'react'; 
import ReactDOM from 'react-dom'; 
import App from './app'; 
import { Provider } from 'react-redux' 
import { createStore } from 'redux' 
import reducers from './reducers' 

const initialState = { 
    tables: { 
     domains: { 
      headers: { 
       id: 'Id', 
       domain: 'Domain', 
       host: 'Hoster', 
      }, 
      items: [ 
       { 
        id: 1, 
        domain: 'dev.example.com', 
        host: 'dev', 
       }, 
       { 
        id: 2, 
        domain: 'prod.example.com', 
        host: 'prod', 
       } 
      ] 
     }, 
     hosts: { 
      headers: { 
       id: 'Id', 
       label: 'Label', 
       type: 'Type', 
       hoster: 'Corporation', 
      }, 
      items: [ 
       { 
        id: 1, 
        label: 'Server 1', 
        type: 'Server type', 
        hoster: 'Gandi', 
       }, 
       { 
        id: 2, 
        label: 'Server 2', 
        type: 'Server type', 
        hoster: 'OVH', 
       } 
      ] 
     } 
    } 
} 

let store = createStore(reducers, initialState) 

ReactDOM.render(
    <Provider store={store}> 
     <App /> 
    </Provider>, 
    document.getElementById('root') 
); 

app.js

import React from 'react'; 
import Domains from './modules/domains.js'; 
import Hosts from './modules/hosts.js'; 
import Nav from './modules/nav.js'; 
import { 
    BrowserRouter as Router, 
    Route, 
} from 'react-router-dom' 

export default class App extends React.Component { 
    render() { 
     return (
      <Router> 
       <div className="container-fluid col-md-6"> 
        <Nav /> 
        <Route path="/domains" component={Domains}/> 
        <Route path="/hosts" component={Hosts}/> 
       </div> 
      </Router> 
     ); 
    } 
} 

domain.js

import React from 'react'; 
import Table from './../components/table.js'; 
import { connect } from 'react-redux' 
import { deleteDomain } from './../actions' 

const mapStateToProps = (state) => { 
    return state.tables.domains 
} 

const mapDispatchToProps = (dispatch) => { 
    return { 
      onRowClick: (id) => { 
       dispatch(deleteDomain(id)) 
      } 
     } 
} 

class DomainsView extends React.Component { 
    render() { 
     return (
      <Table headers={this.props.headers} items={this.props.items} onRowClick={this.props.onRowClick} /> 
     ) 
    } 
} 

const Domains = connect(
    mapStateToProps, 
    mapDispatchToProps, 
)(DomainsView) 

export default Domains 

hosts.js 同用型動物常量/類名domains.js和道具

+1

@mbehrlich是正確的 - 你會改變。請閱讀Redux文檔的[不可變更新模式](http://redux.js.org/docs/recipes/reducers/ImmutableUpdatePatterns.html)以獲取更多信息。 – markerikson

回答

1

由於您的終極版狀態被深度嵌套,Object.assign不發出實際重複。雖然狀態本身是重複的,但它的值是與之前相同的對象的引用。結果,您的深層嵌套對象不會重複。我的建議是使用lodash中的合併方法,而不是使用Reducer中的Object.assign。通過npm安裝lodash然後:

import { combineReducers } from 'redux' 
import { merge } from 'lodash' 
import { DELETE_DOMAIN, DELETE_HOST } from './actions' 

function deleteItem(state, index) { 
    let newState = merge({}, state); 

    newState.items.splice(index, 1) 

    return newState 
} 

function tables(state = {}, action) { 
    let newState; 
    switch (action.type) { 
     case DELETE_HOST: 
      newState = merge({}, state); 
      newState.hosts = deleteItem(state.hosts, action.host) 
      return newState; 
     case DELETE_DOMAIN: 
      newState = merge({}, state); 
      newState.domains = deleteItem(state.domains, action.domain) 
      return newState; 
     default: 
      return state 
    } 
} 

const reducers = combineReducers({ 
    tables, 
}) 

export default reducers 
+0

不可變的更新嵌套結構是令人困惑的。這個答案就像它容易寫/理解一樣。另一種替代方法是平滑你的狀態:http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html。 –

0

你應該「扁平」你的狀態。其中一個原因是不斷更新的狀態,當它包含嵌套的項目時,它可以很快得到詳細和難以理解的狀態。請參閱此主題的Redux文檔中的示例:http://redux.js.org/docs/recipes/reducers/ImmutableUpdatePatterns.html#correct-approach-copying-all-levels-of-nested-data

查看Redux文檔生成平坦狀態的這部分:http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html。你基本上創建一個迷你關係數據庫。在你的例子中,你會將headersitems分離到它們自己的數組中,數組中的每個項目都有一個唯一的ID。對於domainshosts,您將擁有一個密鑰header_idsitem_ids,其中包含引用數據的ID數組。

這簡化了添加和刪除headersitems太多,因爲你只是從ID自己的數組中刪除它們,並從頭部或項目的id domainshosts列表中刪除其ID。

再次,Redux文檔的這一部分將向您顯示如何使用示例來完成此操作:http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html

附註:這不是我最喜歡的Redux!

0

非常感謝。 使用Lodash是解決方案。不過,我壓扁我的狀態是一個很好的做法

這裏有結果:

index.js

... 
const initialState = { 
    tables: { 
     domains_headers: { 
      id: 'Id', 
      domain: 'Domain', 
      host: 'Host', 
     }, 
     domains_items: [ 
      { 
       id: 1, 
       domain: 'domain_1', 
       host: 'host_domain_1', 
      }, 
      { 
       id: 2, 
       domain: 'domain_2', 
       host: 'host_domain_2', 
      } 
     ], 
     hosts_headers: { 
      id: 'Id', 
      label: 'Label', 
      type: 'Type', 
      hoster: 'Corporation', 
     }, 
     hosts_items: [ 
      { 
       id: 1, 
       label: 'label_host_1', 
       type: 'type_host_1', 
       hoster: 'hoster_host_1', 
      }, 
      { 
       id: 2, 
       label: 'label_host_2', 
       type: 'type_host_2', 
       hoster: 'hoster_host_2', 
      } 
     ] 
    } 
} 
... 

reducer.js

import { combineReducers } from 'redux' 
import { DELETE_DOMAIN, DELETE_HOST } from './actions' 
import { merge } from 'lodash' 

function deleteItem(items, index) { 
    items.splice(index, 1) 

    return items 
} 

function tables(state = {}, action) { 
    let newState = merge({}, state) 

    switch (action.type) { 
     case DELETE_HOST: 
      newState.hosts_items = deleteItem(newState.hosts_items, action.host) 
      return newState 

     case DELETE_DOMAIN: 
      newState.domains_items = deleteItem(newState.domains_items, action.domain) 
      return newState 

     default: 
      return state 
    } 
} 

const reducers = combineReducers({ 
    tables, 
}) 

export default reducers 

domains.js

... 
const mapStateToProps = (state) => { 
    return { 
     headers: state.tables.domains_headers, 
     items: state.tables.domains_items 
    } 
} 
... 

hosts.js

... 
const mapStateToProps = (state) => { 
    return { 
     headers: state.tables.hosts_headers, 
     items: state.tables.hosts_items 
    } 
} 
... 
相關問題