React + Redux:reducer 不会重新加载组件
Posted
技术标签:
【中文标题】React + Redux:reducer 不会重新加载组件【英文标题】:React + Redux : reducer won't reload component 【发布时间】:2017-11-07 15:59:51 【问题描述】:我写了一个通用的表格组件,用两个不同的数据集实例化了两次。我使用 Redux 存储来补充道具,并在单击时尝试删除一行。
组件显示良好,onClick 函数被触发,reducer 被调用。新返回的状态返回之前没有单击元素的状态。
我的问题是组件没有重新渲染。 根据故障排除页面(http://redux.js.org/docs/Troubleshooting.html),似乎我没有改变以前的状态(也许我只是不明白该怎么做),我正在调用 dispatch() 函数,而我的 mapStateToProps 似乎是正确的数据在页面加载时显示良好。
我试图在 reducer.js 中定义初始状态 > 表函数,而不是使用 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>
);
域名.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 与 domain.js 相同,但 const / 类名和 props 不同
【问题讨论】:
@mbehrlich 是正确的 - 你正在变异。请阅读 Redux 文档的 Immutable Update Patterns 部分了解更多信息。 【参考方案1】:由于您的 redux 状态是深度嵌套的,因此 Object.assign 不会进行实际复制。尽管状态本身是重复的,但它的值是对与以前相同的对象的引用。因此,您的深层嵌套对象不会重复。我的建议是在你的减速器中使用 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
【讨论】:
不可变地更新嵌套结构令人困惑。这个答案很容易编写/理解。另一种选择是扁平化你的状态:redux.js.org/docs/recipes/reducers/NormalizingStateShape.html.【参考方案2】:你应该“扁平化”你的状态。造成这种情况的原因之一是,当状态包含嵌套项时,不可变更新状态可能会变得冗长且难以快速理解。请参阅 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。您基本上创建了一个小型关系数据库。在您的示例中,您会将headers
和items
分隔到它们自己的数组中,数组中的每个项目都有一个唯一的ID。对于domains
和hosts
,您将拥有header_ids
和item_ids
的键,其中包含引用数据的ID 数组。
这也简化了添加和删除 headers
和 items
的过程,因为您只需按 id 从它们自己的数组中删除它们,然后从 domains
或 hosts
标题或项目 id 列表中删除它们的 id。
再一次,这部分 Redux 文档将通过示例向您展示如何做到这一点:http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html。
旁注:这不是我最喜欢的 Redux 方面!
【讨论】:
【参考方案3】:非常感谢各位。 使用 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
...
【讨论】:
以上是关于React + Redux:reducer 不会重新加载组件的主要内容,如果未能解决你的问题,请参考以下文章
react状态管理器(分模块)之redux和redux + react-redux + reducer和redux + react-redux + reducer分模块 + 异步操作redux-thu