简体   繁体   English

React + Redux:Reducer不会重新加载组件

[英]React + Redux : reducer won't reload component

I wrote a generic Table component, instancied twice with two differents datasets. 我编写了一个通用的Table组件,并用两个不同的数据集实例化了两次。 I use a Redux store to hydrate props and I try to delete a row when I click on it. 我使用Redux商店为道具补水,当我单击它时尝试删除一行。

Components are well displayed and onClick function is fired and reducer is called. 组件显示良好,并触发了onClick函数,并调用了reducer。 The new returned state returns the previous state without the element clicked. 新的返回状态将返回先前的状态,而无需单击元素。

My problem is that the components are not re-rendered. 我的问题是没有重新渲染组件。 According to the troubleshouting page ( http://redux.js.org/docs/Troubleshooting.html ), it seems that I don't mutate the previous state (maybe I just don't understand how to do), I'm calling the dispatch() function and my mapStateToProps seems to be right as datas are well displayed on the page load. 根据故障排除页面( http://redux.js.org/docs/Troubleshooting.html ),看来我没有改变以前的状态(也许我只是不明白该怎么做),调用dispatch()函数和我的mapStateToProps似乎正确,因为数据在页面加载时很好地显示了。

I tried to define initial state in reducer.js > table function instead of doing it with createStore function, I tried to split 'tables' reducer into 'domains' and 'hosts' reducers (duplicate code), I tried to not use combineReducers() as I have only one (but I will have more in the future), and I tried so many things so I can't remember. 我试图在reducer.js> table函数中定义初始状态,而不是使用createStore函数来定义初始状态,我试图将“ tables” reducer拆分为“ domains”和“ hosts” reduces(重复代码),我试图不使用CombineReducers( ),因为我只有一个(但将来还会有更多),并且尝试了很多事情,所以我不记得了。

I bet it's not a big deal but I just not able to figure out what's going on. 我敢打赌这没什么大不了的,但我只是无法弄清楚发生了什么。 Can you help me please ? 你能帮我吗 ? Thank you very much. 非常感谢你。


reducer.js 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 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 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 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 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 Same as domains.js with differents const / class names and props hosts.js与domains.js相同,但具有不同的const /类名和属性

Since your redux state is deeply nested, Object.assign does not make an actual duplicate. 由于您的redux状态是深层嵌套的,因此Object.assign不会进行实际的重复。 Although the state itself is a duplicate, its values are references to the same objects as before. 尽管状态本身是重复的,但其值引用的对象与以前相同。 As a result, your deeply nested objects are not duplicated. 因此,您的深层嵌套对象不会被复制。 My advice is to use the merge method from lodash instead of Object.assign in your reducer. 我的建议是使用lodash中的merge方法,而不是化简器中的Object.assign。 Install lodash via npm and then: 通过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

You should "flatten" your state. 您应该“展平”您的状态。 One of the reasons for this is immutably updating state can get verbose and hard to understand pretty quickly when it contains nested items. 原因之一是,当状态包含嵌套项时,更新状态会变得冗长且难以理解。 See this example in the Redux docs on this topic: 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/ImmutableUpdatePatterns.html#correct-approach-copying-all-levels-nested-data

Check out this part of the Redux docs on producing a flatter state: http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html . 请查看Redux文档中有关产生平坦状态的这部分内容: http : //redux.js.org/docs/recipes/reducers/NormalizingStateShape.html You basically create a mini-relational database. 您基本上是创建一个小型关系数据库。 In your example you would separate headers and items into their own arrays, each item in the array having a unique id. 在您的示例中,您将headersitems分成各自的数组,数组中的每个项目都有唯一的ID。 For domains and hosts you would have a key of header_ids and item_ids which contain an array of ids for the referenced data. 对于domainshosts您将具有header_idsitem_ids键,其中包含用于引用数据的ID数组。

This simplifies adding and removing headers and items too because you just delete them by id from their own array and remove their id from the domains or hosts list of header or item ids. 这也简化了headersitems添加和删​​除,因为您只需按ID从它们自己的数组中删除它们,然后从它们的domainshosts列表中删除标题或项目ID。

Again, this part of the Redux docs will show you exactly how to do this with examples: http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html . 同样,Redux文档的这一部分将通过示例向您确切演示如何执行此操作: http : //redux.js.org/docs/recipes/reducers/NormalizingStateShape.html

Side note: this is not my favorite aspect of Redux! 旁注:这不是Redux我最喜欢的方面!

Thanks a lot guys. 非常感谢。 Using Lodash was the solution. 解决方案是使用Lodash。 However, I flattened my state as a good practice 但是,我把状态扁平化是一种好习惯

Here are the result : 结果如下:

index.js 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 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 domains.js

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

hosts.js hosts.js

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM