Reactjs 连接的组件不';从另一个连接的组件更改存储后重新渲染
我遇到了一个与redux相关的问题。 我有两个连接的组件,它们是:Reactjs 连接的组件不';从另一个连接的组件更改存储后重新渲染,reactjs,redux,react-redux,Reactjs,Redux,React Redux,我遇到了一个与redux相关的问题。 我有两个连接的组件,它们是: 头像位于导航栏中,始终可见 负责更改商店中化身图像的配置文件 如果我是对的,当存储更改时,如果需要,任何连接的组件都将重新渲染 在我的例子中,当action UPDATE_CURRENT_用户更新化身图像时,navbar化身只有在我更改路由或重新加载页面后才会获得新图像 我找到了一个解决方案,但很多人说这是一个黑客, 我在主组件中设置了一个侦听器来存储更改,并执行了forceUpdate() 我不想使用它,因为连接的组件应该在存
forceUpdate()
我不想使用它,因为连接的组件应该在存储更改时重新渲染
用户操作:
export const getCurrentUser = () => dispatch => {
axios.get("user").then(user => {
dispatch({
type: GET_CURRENT_USER,
payload: user.data
});
});
};
export const updateCurrentUser = user => dispatch => {
dispatch({
type: UPDATE_CURRENT_USER,
payload: user
})
}
用户减速器
const initialState = {
user: {}
}
export default function (state = initialState, action) {
switch (action.type) {
case GET_CURRENT_USER:
return { ...state, user: action.payload };
case UPDATE_CURRENT_USER:
return { ...state, user: action.payload }
default:
return state;
}
}
轮廓组件
class Profile extends Component {
render() {
const { currentUser, updateCurrentUser } = this.props;
return (
<div id="profile-container">
<ProfileSider
currentUser={currentUser}
updateCurrentUser={updateCurrentUser}
/>
<ProfileContent
currentUser={currentUser}
updateCurrentUser={updateCurrentUser}
/>
</div>
);
}
}
const mapStateToProps = state => ({
currentUser: state.userReducer.user
});
export default connect(
mapStateToProps,
{ updateCurrentUser }
)(Profile);
类配置文件扩展组件{
render(){
const{currentUser,updateCurrentUser}=this.props;
返回(
);
}
}
常量mapStateToProps=状态=>({
当前用户:state.userReducer.user
});
导出默认连接(
MapStateTops,
{updateCurrentUser}
)(概况);
配置文件的子配置文件侧栏
class ProfileSider extends Component {
state = { uploading: false };
triggerAvatarInput() {
$("#avatarInput").click();
}
handleChange = async event => {
this.setState({ ...this.state, uploading: true });
const avatarFormData = new FormData();
avatarFormData.append("file", event.target.files[0]);
axios
.post("uploadFile", avatarFormData)
.then(res => {
const avatarURIFormData = new FormData();
avatarURIFormData.append("avatar", res.data.fileDownloadUri);
axios
.put("user/update", avatarURIFormData)
.then(res => {
const { currentUser } = this.props;
currentUser.avatar = res.data.avatar;
this.props.updateCurrentUser(currentUser);
this.setState({
...this.state,
uploading: false,
avatar: currentUser.avatar
});
message.success("Avatar updated successfully", 3);
})
.catch(error => {
this.setState({ ...this.state, uploading: false });
message.error("Updating avatar failed!", 3);
});
})
.catch(error => {
this.setState({ ...this.state, uploading: false });
message.error("Uploading avatar failed!", 3);
});
};
render() {
const { uploading } = this.state;
const { currentUser } = this.props;
return (
<div id="profile-sider">
<div id="profile-sider-info">
<div id="profile-sider-info-avatar">
<div className="container">
<div
className="overlay-uploading"
className={
uploading ? "overlay-uploading" : "overlay-uploading hidden"
}
>
<Icon type="loading" style={{ fontSize: 50, color: "#FFF" }} />
</div>
<div className="overlay" />
<div className="overlay-text" onClick={this.triggerAvatarInput}>
<Icon type="camera" style={{ fontSize: 20 }} />
<span>Update</span>
</div>
<div
className="avatar"
style={{
backgroundImage: "url(" + currentUser.avatar + ")"
}}
></div>
<input
onChange={this.handleChange}
type="file"
accept="image/png, image/jpeg, image/jpg"
id="avatarInput"
/>
</div>
</div>
<h2 style={{ marginTop: 20, textAlign: "center" }}>
{currentUser.fullName}
</h2>
<h4 style={{ textAlign: "center" }}>{currentUser.email}</h4>
</div>
<div id="profile-sider-actions">
<div className="profile-sider-actions-item">
<Link to="/profile/courses" style={{ transition: 0 }}>
<Button type="primary" id="courses-btn">
<Icon type="read" style={{ marginRight: 15 }} />
My Courses
</Button>
</Link>
</div>
<div className="profile-sider-actions-item">
<Link to="/profile/update">
<Button type="primary" id="update-infos-btn">
<Icon type="sync" style={{ marginRight: 15 }} />
Update Infos
</Button>
</Link>
</div>
</div>
</div>
);
}
}
export default ProfileSider;
class ProfileSider扩展组件{
状态={上传:false};
triggerAvatarInput(){
$(“#avatarInput”)。单击();
}
handleChange=异步事件=>{
this.setState({…this.state,uploading:true});
const avatarFormData=新表单数据();
avatarFormData.append(“文件”,event.target.files[0]);
axios
.post(“上传文件”,avatarFormData)
。然后(res=>{
const avatarURIFormData=新表单数据();
append(“avatar”,res.data.fileDownloadUri);
axios
.put(“用户/更新”,avatarURIFormData)
。然后(res=>{
const{currentUser}=this.props;
currentUser.avatar=res.data.avatar;
this.props.updateCurrentUser(currentUser);
这是我的国家({
…这个州,
上传:错,
化身:currentUser.avatar
});
message.success(“化身更新成功”,3);
})
.catch(错误=>{
this.setState({…this.state,上载:false});
错误(“更新化身失败!”,3);
});
})
.catch(错误=>{
this.setState({…this.state,上载:false});
消息。错误(“上传化身失败!”,3);
});
};
render(){
const{upload}=this.state;
const{currentUser}=this.props;
返回(
更新
{currentUser.fullName}
{currentUser.email}
我的课程
更新信息
);
}
}
导出默认分析器;
位于导航栏中的化身组件
class ProfileAvatar extends Component {
constructor() {
super();
this.handleClick = this.handleClick.bind(this);
this.handleOutsideClick = this.handleOutsideClick.bind(this);
this.state = {
showProfileDropdown: false
};
}
componentDidMount() {
this.props.getCurrentUser();
}
handleLogout = async () => {
try {
await auth.logout();
this.props.onLogout();
notification["success"]({
message: "You have been successfully logged out!"
});
} catch (ex) {}
};
handleClick() {
if (!this.state.showProfileDropdown) {
// attach/remove event handler
document.addEventListener("click", this.handleOutsideClick, false);
} else {
document.removeEventListener("click", this.handleOutsideClick, false);
}
this.setState(prevState => ({
showProfileDropdown: !prevState.showProfileDropdown
}));
}
handleOutsideClick(e) {
// ignore clicks on the component itself
if (this.element && this.element.contains(e.target)) {
return;
}
this.handleClick();
}
render() {
const { currentUser } = this.props;
return (
<div
className="profile-avatar"
ref={element => {
this.element = element;
}}
>
<Avatar
onClick={this.handleClick}
size="large"
style={{ color: "#f56a00", backgroundColor: "#fde3cf" }}
src={currentUser.avatar}
>
{currentUser.fullName ? currentUser.fullName.charAt(0) : null}
</Avatar>
{this.state.showProfileDropdown && (
<div className="profile-dropdown-list">
<List
className="dropdown_list dropdown-shadow "
size="small"
style={{ width: "150px" }}
bordered
itemLayout="vertical"
dataSource={[
<Link to="/profile/update" className="profile-list-item">
<List.Item className="list-item">
<Icon className="profile-icons" type="user" /> My Profile
</List.Item>
</Link>,
<Link to="/profile/courses" className="profile-list-item">
<List.Item className="list-item">
<Icon className="profile-icons" type="container" /> My
Courses
</List.Item>
</Link>,
<List.Item className="list-item">
<Icon className="profile-icons" type="question-circle" /> Ask
for Help
</List.Item>,
<List.Item className="list-item" onClick={this.handleLogout}>
<Icon className="profile-icons" type="logout" /> Log out
</List.Item>
]}
renderItem={item => item}
/>
</div>
)}
</div>
);
}
}
const mapStateToProps = state => ({
currentUser: state.userReducer.user
});
export default connect(
mapStateToProps,
{ getCurrentUser }
)(ProfileAvatar);
class profiler扩展组件{
构造函数(){
超级();
this.handleClick=this.handleClick.bind(this);
this.handleOutsideClick=this.handleOutsideClick.bind(this);
此.state={
ShowProfile下拉列表:false
};
}
componentDidMount(){
this.props.getCurrentUser();
}
handleLogout=async()=>{
试一试{
等待身份验证注销();
this.props.onLogout();
通知[“成功”]({
消息:“您已成功注销!”
});
}捕获(ex){}
};
handleClick(){
如果(!this.state.showProfile下拉列表){
//附加/删除事件处理程序
document.addEventListener(“单击”,this.handleOutsideClick,false);
}否则{
document.removeEventListener(“单击”,this.handleOutsideClick,false);
}
this.setState(prevState=>({
showProfileDropdown:!prevState.showProfileDropdown
}));
}
把手外侧咔嗒声(e){
//忽略对组件本身的单击
if(this.element&&this.element.contains(e.target)){
返回;
}
这个。handleClick();
}
render(){
const{currentUser}=this.props;
返回(
{
this.element=元素;
}}
>
这里有两个问题:
- 您正在更改存储区中的现有对象
- 在分派操作时,您正在将完全相同的用户对象发送回存储区
具体而言,以下几行是原因:
const{currentUser}=this.props;
currentUser.avatar=res.data.avatar;
this.props.updateCurrentUser(currentUser);
currentUser
是已在Redux存储中的用户对象。此代码会对该对象进行变异,并将其插入存储中
这导致连接的组件认为实际上什么都没有改变
解决此问题的最短方法是创建一个新的用户对象,并插入:
const{currentUser}=this.props;
const updateuser={…currentUser,avatar:res.data.avatar};
this.props.updateCurrentUser(updateUser);
为了避免将来出现这种情况,我强烈建议您使用,它可以检测突变,如果发生突变,将抛出错误
class ProfileAvatar extends Component {
constructor() {
super();
this.handleClick = this.handleClick.bind(this);
this.handleOutsideClick = this.handleOutsideClick.bind(this);
this.state = {
showProfileDropdown: false
};
}
componentDidMount() {
this.props.getCurrentUser();
}
handleLogout = async () => {
try {
await auth.logout();
this.props.onLogout();
notification["success"]({
message: "You have been successfully logged out!"
});
} catch (ex) {}
};
handleClick() {
if (!this.state.showProfileDropdown) {
// attach/remove event handler
document.addEventListener("click", this.handleOutsideClick, false);
} else {
document.removeEventListener("click", this.handleOutsideClick, false);
}
this.setState(prevState => ({
showProfileDropdown: !prevState.showProfileDropdown
}));
}
handleOutsideClick(e) {
// ignore clicks on the component itself
if (this.element && this.element.contains(e.target)) {
return;
}
this.handleClick();
}
render() {
const { currentUser } = this.props;
return (
<div
className="profile-avatar"
ref={element => {
this.element = element;
}}
>
<Avatar
onClick={this.handleClick}
size="large"
style={{ color: "#f56a00", backgroundColor: "#fde3cf" }}
src={currentUser.avatar}
>
{currentUser.fullName ? currentUser.fullName.charAt(0) : null}
</Avatar>
{this.state.showProfileDropdown && (
<div className="profile-dropdown-list">
<List
className="dropdown_list dropdown-shadow "
size="small"
style={{ width: "150px" }}
bordered
itemLayout="vertical"
dataSource={[
<Link to="/profile/update" className="profile-list-item">
<List.Item className="list-item">
<Icon className="profile-icons" type="user" /> My Profile
</List.Item>
</Link>,
<Link to="/profile/courses" className="profile-list-item">
<List.Item className="list-item">
<Icon className="profile-icons" type="container" /> My
Courses
</List.Item>
</Link>,
<List.Item className="list-item">
<Icon className="profile-icons" type="question-circle" /> Ask
for Help
</List.Item>,
<List.Item className="list-item" onClick={this.handleLogout}>
<Icon className="profile-icons" type="logout" /> Log out
</List.Item>
]}
renderItem={item => item}
/>
</div>
)}
</div>
);
}
}
const mapStateToProps = state => ({
currentUser: state.userReducer.user
});
export default connect(
mapStateToProps,
{ getCurrentUser }
)(ProfileAvatar);