【问题标题】:State is returning new state without touching the state REACT/REDUX状态正在返回新状态而不触及状态 REACT/REDUX
【发布时间】:2017-12-08 09:13:46
【问题描述】:

在我开始发布代码之前,我将解释我想要发生什么以及正在发生什么。

我整天都在处理这个问题,终于找到了工作。问题是我不知道它为什么起作用,以及它是否是最好的方法。 我想要发生的是,当用户在评论上单击删除时,我希望从后端的当前 post 和前端的状态中删除该评论。

更新:我并没有真正添加足够的内容来显示我在说什么。当用户单击删除按钮时,它将在此处触发此功能:

deleteComment(comment) {
    const {id} = this.props.match.params;
    const {user, post, auth} = this.props;

    if(!user) {
      return (<div></div>);
    }

    if(auth) {
      if(user._id === comment.author.id){
        this.props.deleteComments(post, comment._id, () => {
          this.props.history.push(`/posts/${post._id}`);
        });
      }
    }
  }

然后调用动作:

export function deleteComments(post, comment_id, cb) {
  return function(dispatch) {
    axios.delete(`${ROOT_URL}/${post._id}/comments/${comment_id}`)
      .then(() => {
        dispatch({
          type: DELETE_COMMENTS,
          payload: comment_id,
          post: post
        });
      })
      .then(() => cb())
      .catch((error) => {
        console.log(error);
      });
  }
}

我在想什么: 上面的所有代码真的都有效,所以它更像是一个参考。我想知道为什么在减速器中DELETE_COMMENTS 被开关盒捕获时,图片下方的当前状态如何在没有我删除的评论的情况下更改为新状态。我没有明确地编辑或更改状态,那么return (state); 是如何在此处实际更改它的?

详细说明我目前的状态如下:

如您所见,我有一个帖子,里面有 cmets。 cmets 存储在一个数组中,它们中的每一个都是具有 text、author.id、author.email 和 createdAt 值的对象。现在,正如您在 reducer_post 中看到的那样,当动作被触发时,它会发送 DELETE_COMMENTS 调度类型并更改状态。

寻求帮助:action.payload 是 comment._id,action.post 是当前正在查看的帖子。

import {
  GET_ALL_POSTS,
  GET_POST,
  CREATE_POST,
  DELETE_POST,
  UPDATE_POST,
  DELETE_COMMENTS
} from '../actions/types';
import _ from 'lodash';

export default function(state = {}, action) {
  switch(action.type) {
    case GET_ALL_POSTS:
      return _.mapKeys(action.payload.data, '_id');
    break;
    case GET_POST:
      return {...state, [action.payload.data._id]: action.payload.data};
    break;
    case DELETE_POST:
      return _.omit(state, action.payload);
    break;
    case DELETE_COMMENTS:
      let newCommentArray = _.reject(action.post.comments, {'_id': action.payload});
      let newPost = action.post;
      newPost.comments = newCommentArray;
      return (state);
    case UPDATE_POST:
      let updates = {[action.payload.data._id]: action.payload.data};
      return _.merge(...state, updates);
    break;
    default:
      return state;
    break;
  }
}

我知道后端工作得很好,这也工作得很好。我的问题是它如何更新状态,我只需输入return (state);。我不必做类似return _.merge(...state, newPost); 或类似的事情。这一切如何运作得很好,不,我不只是复制一些教程并来到这里问这个只是为了弄清楚其他人是如何做到的。目前我相信魔法,所以解释一下会很好。

既然我们在这里,这是最好的方法吗?或者这里有更好更干净的做事方式。

【问题讨论】:

  • 我有点困惑。您是说default 案例中的状态正在被修改?或者当 API 响应返回时您正在调度一个动作?如果是这样,您要调度什么操作?
  • 让我添加一些编辑,我可以看到这是令人困惑的地方抱歉。
  • 可能是因为state 包含对comments 的对象引用,例如state["postid"].comments === action.post.comments 或类似的东西。
  • 所以改变 action.post 会改变状态?
  • 我认为您只是不了解 Redux,请尝试阅读它。每次更改存储中的数据时,都会调用订阅组件的回调,状态更改并重新渲染。

标签: javascript node.js reactjs redux lodash


【解决方案1】:

我认为您实际上是在直接改变状态,并且在组件仍在更新的意义上以某种方式摆脱它。

让我们跟随逻辑的轨迹:

  1. 在deleteComment() 中,您引用this.props.post。该对象可能是通过mapStateToProps 从存储中提取的,因此它是存储中的对象引用。
  2. post 被传递给 this.props.deleteComments(post)
  3. deleteComments() 发送 ({type : DELETE_COMMENTS, post})
  4. 在减速器中,您有let newPost = action.post; newPost.comments = newCommentArray。但是,此时,newPost 仍指向已处于存储状态的完全相同的对象。所以,你直接对其进行了变异。

通常,这种突变会导致您的组件不重新渲染,所以我只能假设在mapState 中提取的其他一些值也得到了正确更新,从而导致重新渲染。

正如 Redux 文档的 Structuring Reducers - Immutable Update Patterns 页面中所讨论的,let someVariable = anotherVariable 不会创建新的对象引用 - 它只是创建第二个变量,该变量指向 same 对象引用。正确的不可变更新需要您正在使用的每个嵌套级别的副本,而不仅仅是一个级别。在您的情况下,您通过_.reject() 创建了一个新数组,但您没有创建post 对象的副本。

所以,最后,你的 reducer 逻辑是不正确的,你的应用程序有点意外地工作了 :)

更新

回答您关于正确更新的问题:您需要创建post 对象的副本 并使用新的 cmets 数组对其进行更新,并且您需要复制 state 对象并用新的 post 对象更新它。根据其余代码的作用,我猜这应该可行:

case DELETE_COMMENTS:
     const newCommentArray = _.reject(action.post.comments, {'_id': action.payload});

     const newState = {
         ...state,
         [action.post.id] : {
             ...action.post,
             comments : newCommentArray
         }
     };

     return newState;

【讨论】:

  • 我将如何正确执行此操作?如果您不介意回答这个问题,其他人也会在看到此内容时知道。
  • 我查看了文档,嵌套级别的数据让我很困惑。
  • 更新我的答案以展示正确的不可变更新。
  • 谢谢!我会试试这个,但这对我来说意义重大,哈哈
  • 所以可以说我需要编辑坐在 cmets 上的作者。我必须去另一个层次?所以像const newState = { ...state, [action.post.id] : { ...action.post, comments : newCommentArray, ...action.post.comments, author: newAuthor } };
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-28
相关资源
最近更新 更多