首页 文章

React Redux:reducer不会重新加载组件

提问于
浏览
0

我写了一个通用的Table组件,用两个不同的数据集进行了两次实例化 . 我使用Redux商店来保湿道具,当我点击它时我尝试删除一行 .

组件显示良好,触发onClick函数并调用reducer . 新返回的状态返回先前的状态,而不单击该元素 .

我的问题是组件不会重新渲染 . 根据疑难解答页面(http://redux.js.org/docs/Troubleshooting.html),似乎我不知道怎么做了,我正在调用dispatch()函数,我的mapStateToProps似乎是正确的,因为数据在页面加载时很好地显示 .

我尝试在reducer.js> table函数中定义初始状态,而不是使用createStore函数,我尝试将'tables'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与具有不同的const / class名称和props的domains.js相同

3 回答

  • 0

    由于您的redux状态是深度嵌套的,因此Object.assign不会产生实际的重复 . 虽然状态本身是重复的,但它的值是对以前相同对象的引用 . 因此,您的深层嵌套对象不会重复 . 我的建议是在你的reducer中使用lodash中的merge方法而不是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
    
  • 1

    你应该"flatten"你的州 . 造成这种情况的原因之一是,当包含嵌套项时,状态可以变得冗长且难以理解 . 请参阅有关此主题的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中删除它们自己的数组,并从 Headers 或项目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
        }
    }
    ...
    

相关问题