React Redux Store 更新,但组件不重新渲染
Posted
技术标签:
【中文标题】React Redux Store 更新,但组件不重新渲染【英文标题】:React Redux Store updating, but component not re-rendering 【发布时间】:2017-06-17 06:44:08 【问题描述】:使用终端测试我发送的操作,Redux-logger 显示我的状态正在正确更新。但是,由于状态更改,我的组件不会重新渲染。我查看了关于组件不重新渲染的 SO 答案,大多数回复声称状态正在发生变化;因此,Redux 不会重新渲染。但是,我正在使用 Lodash 的合并来对对象进行深度复制,我很确定我没有返回修改后的对象。 (请看下面附上的sn-p)
很想听听你们的一些建议,把我的头发拉出来!
const usersReducer = (state = , action) =>
Object.freeze(state); // avoid mutating state
console.log(state);
// returns an empty object
let newState = merge(, state);
console.log(newState);
// returns my state with my dispatched action object inside already???
// newState for some reason already has new dispatched action
switch (action.type)
case RECEIVE_USER:
let newUser = [action.user.id] = action.user;
return merge(newUser, newUser);
case RECEIVE_USERS:
newState = ;
action.users.forEach(user =>
newState[user.id] = user;
);
return merge(, newState);
default:
return state;
;
反应容器组件
import connect from 'react-redux';
import receiveUsers, receiveUser, refreshAll, requestUsers, requestUser from '../../actions/user_actions';
import allUsers from '../../reducers/selectors';
import UserList from './user_list';
const mapStateToProps = (state) => (
users: allUsers(state), // allUsers (selector that takes the state specfically the user Object and returns an array of user Objects)
state
);
const mapDispatchToProps = (dispatch) => (
requestUser: () => dispatch(requestUser()),
requestUsers: () => dispatch(requestUsers()),
receiveUsers: (users) => dispatch(receiveUsers(users)),
receiveUser: (user) => dispatch(receiveUser(user)),
refreshAll: (users) => dispatch(refreshAll(users))
);
export default connect(
mapStateToProps,
mapDispatchToProps
)(UserList);
React 展示组件
import React from 'react';
class UserList extends React.Component
render()
const users, state = this.props;
const userItems = users.map((user, idx) =>
return(<li key=idx>user.username</li>);
);
return (
<div>
<ul>
userItems
</ul>
</div>
);
export default UserList;
反应商店
import createStore, applyMiddleware from 'redux';
import createLogger from 'redux-logger';
import RootReducer from '../reducers/root_reducer';
const logger = createLogger();
const configureStore = (preloadedState = ) =>
return createStore(
RootReducer,
preloadedState,
applyMiddleware(logger));
;
// const configureStore = createStore(rootReducer, applyMiddleware(logger));
// oddly enough, when I have the store as a constant and not a function that returns the store constant, dispatching actions through the terminal will correctly update the state and rerender the component
export default configureStore;
反应选择器
const allUsers = ( users ) =>
return Object.keys(users).map(id => (
users[id]
));
;
export default allUsers;
【问题讨论】:
你能添加你的组件代码吗?将组件连接到商店的方式可能存在问题。 这可能与您的问题无关,但是您对合并的任何使用对我来说都没有任何意义。您到底希望完成什么?我建议您只使用 splats,例如return ...state, x: 42
在哪里添加usersReducer
到商店?您能否验证您的组件 mapStateToProps
在商店状态更改时是否被调用?如果不是,则可能意味着您的减速器未正确执行。
@Mustafa 在我的 RootReducer 我调用 CombineReducers( users: usersReducer );我认为您正在做某事,似乎没有调用 mapStateToProps,但是当您调度操作时不会自动调用它吗?
@Tonyhliu 也许是因为您没有在应用程序的任何地方调用 configureStore .. 我还没有看到商店是这样创建的,所以不确定它是否可能。这个return merge(newUser, newUser);
对我来说也没有意义。
【参考方案1】:
我花了很多时间发现当使用超过 1 个 reducer(使用 combineReducers)时,您的 mapStateToProps 应该指向正确的 reducer 名称,例如
const mapStateToProps = state => (
someVar: state.yourReducerName.someVar,
loading: state.yourReducerName.loading,
error: state.yourReducerName.error
);
【讨论】:
【参考方案2】:从 prop 状态创建新的数组副本以重新渲染组件
render()
const allPost = this.props;
//Use the spread operator to create a new copy of the array
const posts = [...allPost];
const plansList = () =>
return posts.length < 1 ? null : posts && <PlansList allPost=posts
/>;
;
return (
<>
<Container className="mt-lg-5 pt-lg-5">
plansList()
</Container>
</>
);
【讨论】:
【参考方案3】:这种情况下的常见问题是使用非反应性操作来更改状态。例如对数组使用 concat(),而不是 push() 等等。
【讨论】:
谢谢!这对我来说是一个很大的痛苦!使用推送不会更新状态。使用扩展运算符const x = [...x, y]
或使用 concat....【参考方案4】:
我遇到了类似的问题,以防万一有人偶然发现,我需要克隆数组以便重新渲染视图:
export const addFieldRow = () => (
(dispatch: any, getState: any) =>
const state = getState();
const myArrayOfObjects = myArrayOfObjectsProp(state);
const newObject =
key: "",
value: "",
;
myArrayOfObjects.push(newObject);
dispatch(addFieldRowAction( myArrayOfObjects: [...myArrayOfObjects] )); <== here
);
【讨论】:
你让我头疼 :-) 对于那些想要如何制作浅拷贝的人:freecodecamp.org/news/… 你节省了我一天【参考方案5】:我使用这个解决方案来做到这一点。 我将用户置于我的状态,并使用 componentWillReceiveProps 在任何更改时对其进行更新。希望它有所帮助:-)
class UserList extends React.Component
constructor(props)
super(props);
console.log(this.props);
this.state =
users: props.users
;
componentWillReceiveProps(nextProps)
if (this.props.users !== nextProps.users)
this.setState(
users: nextProps.users,
);
render()
const users = this.state;
const userItems = users.map((user, idx) =>
return(<li key=idx>user.username</li>);
);
return (
<div>
<ul>
userItems
</ul>
</div>
);
export default UserList;
【讨论】:
componentWillReceiveProps
是 React v16 中基于类/容器组件的不安全方法
这个问题是关于 Redux 的,你的解决方案没有以任何方式包含它。【参考方案6】:
你的 React 组件是什么样的?您是在其中使用内部状态还是使用道具来下推数据。通常我看到问题在于人们使用 Redux 状态设置道具的内部状态。您应该将道具下推到组件,它们将在更新时重新渲染。
另外,请查看 https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en
看看道具是不是真的变了。
【讨论】:
以上是关于React Redux Store 更新,但组件不重新渲染的主要内容,如果未能解决你的问题,请参考以下文章
在 react js 功能组件中调度操作后如何立即在 redux store 中使用更新的值