React Developer Tools显示数据已更新,组件却没刷新?这不是reducer的坑!
先说结论
view 层未刷新,问题不在 redux,而是 react!react会在每次 state 变更时重渲染。那如何判别是否变更?一句话:基本数据类型直接比较,引用类型比较前后引用是否相等
,所以,必须保证,redux 数据树根结点(肯定是引用类型)每次渲染都不一样!也就有如下写法:
注意reducer中return回去的内容,按以下方式书写
switch (action.type) {
case 'change_is_login': {
const thisAction: ChangeIsLoginAction = action;
const {
isLogin
} = thisAction.payload;
`return {
...state,
isLogin
};`
}
Header组件如图
chrome的redux插件显示数据如图
显示正常,Header代码如下
import React, { Fragment } from 'react';
import { Link } from 'react-router-dom';
import { useSelector, useDispatch, shallowEqual } from 'react-redux';
import { RootState, HeaderState } from '../../types';
import { actionCreators } from './store';
const Header: React.FC = () => {
const dispatch = useDispatch();
const headerState = useSelector<RootState, HeaderState>(state => { return state.header })
console.log('headerPage receive state is');
console.log(headerState);
const {
isLogin,
} = headerState
const handleLogin = () => {
dispatch(actionCreators.login());
}
const handleLogout = () => {
dispatch(actionCreators.logout());
}
return (
<div>
<Link to='/'>首页</Link>
{`islogin: ${isLogin}`}
<br />
{
isLogin ?
<Fragment>
<Link to='/login'>翻译</Link>
<br />
<div onClick={handleLogout}>退出</div>
</Fragment>
: <div onClick={handleLogin}>登陆</div>
}
</div >
)
};
export default Header;
对应login,logout事件的reducer,目的是:
改变state中isLogin的值,从而刷新Header
- 点击登陆出发点击时间,重设isLogin的action被dispatch至此处
- 定义新变量newState指向state
- 修改newState.isLogin
- 将newState返回
const defaultState: HeaderState = {
isLogin: true,
}
export default (state = defaultState, action: any) => {
switch (action.type) {
case 'change_is_login': {
const thisAction: ChangeIsLoginAction = action;
const {
isLogin
} = thisAction.payload;
const newState = state;
newState.isLogin = isLogin;
return newState;
}
default:
return state;
}
}
逻辑清晰,代码简单看似一气呵成
,点击登陆一看!
Header组件并未刷新
chrome的redux插件显示,isLogin已经改变。
难道useSelector失效了? 笔者翻找react-redux的仓库,并未找到useSelector失效的issue
经过两个小时的dubug,最终定位到reducer代码,更改如下
const defaultState: HeaderState = {
isLogin: true,
}
export default (state = defaultState, action: any) => {
switch (action.type) {
case 'change_is_login': {
const thisAction: ChangeIsLoginAction = action;
const {
isLogin
} = thisAction.payload;
return {
...state,
isLogin
};
}
default:
return state;
}
}
之后,操作回归正常,useSelector生效,组件正常刷新
点击登陆
切换正常。
原因
reducer内数据是不可更改的
,官方推荐两种写法更新state
- 使用
Object.assign({ }, state, {
你要添加的内容
})
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
subState
})
2.当然还有更简洁的写法,ES6的扩展运算符
case SET_VISIBILITY_FILTER:
return {
...state,
`subState`
}
建议读一读redux的文档。
中文文档如下 https://www.redux.org.cn/docs...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。