存储从另一个连接的组件更改后,不重新渲染连接的组件

时间:2019-08-30 23:13:02

标签: reactjs redux react-redux

我遇到与redux相关的问题。 我有2个连接的组件,分别是:

位于导航栏中的1头像始终可见

2-profile wich负责更改商店中的头像图片

如果我是对的,那么当商店改变时,如果需要,任何连接的组件都将重新呈现。 就我而言,当UPDATE_CURRENT_USER操作更新头像图片时,导航栏头像仅在更改路线或重新加载页面后才获取新图片。

我找到了解决方案,但很多人说这是黑客, 我已经在主要组件的商店更改中放置了一个侦听器,并进行了forceUpdate()

    store.subscribe(res => this.forceUpdate());
  }

而且我不愿意使用它,因为连接的组件应该在商店更改时重新呈现。 预先感谢您的帮助。

用户操作:

  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
  })
}

user reducer

    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;
    }
}

个人资料组件

  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);

个人资料的个人资料侧边栏子项

  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 successfuly", 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;

导航栏中的头像组件

  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);

图片:https://imge.to/i/vywTNj

1 个答案:

答案 0 :(得分:0)

这里有两个问题:

  • 您正在从商店中更改现有对象
  • 您在分派操作时将那个完全相同的用户对象发送回商店。

具体来说,以下几行是原因:

            const { currentUser } = this.props;
            currentUser.avatar = res.data.avatar;
            this.props.updateCurrentUser(currentUser);

currentUser是Redux存储中已经存在的用户对象。这段代码会变异该对象,然后将其重新插入商店。

这导致连接的组件认为实际上没有任何更改。

解决此问题的最短方法是创建一个新的用户对象,并将其插入:

const {currentUser} = this.props;
const updatedUser = {...currentUser, avatar: res.data.avatar};
this.props.updateCurrentUser(updatedUser);

为避免将来再次出现这种情况,我强烈建议您使用the configureStore function from our Redux Starter Kit package,它可以检测到突变,如果您突变,则会抛出错误。