Redux 状态更改:UI 仅在我在 rootPath 上时更新

Redux State Change: UI only updates if i'm on rootPath

我有一个类似 Reddit 的应用程序。我正在尝试建立投票功能的地方。我以为我已经解决了它,因为它在 /

时效果很好

但是,如果我进入不同的路径 /:category :/category/:id 我可以看到点击发送的调度,但在这里我必须 "force update" (f5) 才能看到 UI 变化。

API 文件

export const submitPostVote = (option, id) => {
    return axios.post(`${API_URL}/posts/${id}`, {option}, { headers })
}

Action Creator(使用 redux-thunk)

export function postPostVote(option, id) {
    const request = API.submitPostVote(option, id)

    return (dispatch) => {
        request.then(({data}) => {
            dispatch({type: SUBMIT_POST_VOTE, payload: data})
        });
    };
}

减速器

export default function(state = {}, action) {
  const {payload} = action
  switch (action.type){
        case SUBMIT_POST_VOTE:
            return {...state, [payload.id]: payload}

使用它的组件

import { postPostVote } from '../actions';

<span
 className='fa fa-angle-up voteArrow'
 onClick={() => this.props.postPostVote('upVote', id)}
></span>

export default connect(null, {postPostVote})(PostComponent);

在其他组件中导入如下

import PostComponent from './Post_PostComponent';
<div>
                <Container>
                    <PostComponent
                        key={id}
                        id={id}
                        title={title}
                        author={author}
                        voteScore={voteScore}
                        category={category}
                        timestamp={timestamp}
                        redirect={false}
                    />
                </Container>
            </div>

回购

Readable Repo

我想我明白了你的问题,你对两个页面使用了相同的 reducer。

  1. 包含项目列表的页面,在本例中为 reducer 形状 是一个对象,每个键都是一个 id 项,它是一个 object 以及保存该项目的所有数据。
    { '6ni6ok3ym7mf1p33lnez': { author: "thingone", body: "Just kidding. It takes more than 10 minutes to learn technology.", category: "redux", deleted: false, id: "6ni6ok3ym7mf1p33lnez", timestamp: 1468479767190, title: "Learn Redux in 10 minutes!", voteScore: 6 } }

  2. 包含单个项目的页面,在本例中是相同的缩减器 需要处理 object 的不同形状,其中所有 项目的属性是分散的。

{ author: "thingone", body: "Just kidding. It takes more than 10 minutes to learn technology.", category: "redux", deleted: false, id: "6ni6ok3ym7mf1p33lnez", timestamp: 1468479767190, title: "Learn Redux in 10 minutes!", voteScore: 6 }

举个例子,如果您要更改 reducer_posts.js returns:
对象的形状 来自:

case SUBMIT_POST_VOTE:
            return {...state, [payload.id]: payload}

为此:

case SUBMIT_POST_VOTE:
            return {...state, ...payload}  

您会注意到,现在带有列表的第一页无法正常工作,但显示单个项目的第二页可以按预期工作。

因此您应该重新考虑减速器的形状或将此减速器一分为二。

编辑
我很好奇处理这种情况的最佳结构是什么,所以我冒昧地为你改变了一些东西。

所以我决定将您的 reducer_posts.js 分成 2 个减速器:
postspost(复数和单数)。 我添加了另一个减速器 reducer_post.js.
这是代码:

import { POST_GET_POST, SUBMIT_POST_VOTE } from '../actions/action_constants';

export default function (state = {}, action) {
    const { payload } = action
    switch (action.type) {
        case SUBMIT_POST_VOTE:
            return { ...state, ...payload }
        case POST_GET_POST:
            return payload;
        default:
            return state;
    }
}

而旧的减速器 reducer_posts.js 现在看起来像这样:

import _ from 'lodash';
import { POST_GET_POSTS, SUBMIT_POST_VOTE } from '../actions/action_constants';

export default function(state = {}, action) {
  const {payload} = action
  switch (action.type){
        case SUBMIT_POST_VOTE:
            return {...state, [payload.id]: payload}
        case POST_GET_POSTS:
      return _.mapKeys(payload, 'id');
    default:
      return state;
  }
}

当然不要忘记将其添加到 rootReducer

export const rootReducer = combineReducers({
  routing: routerReducer,
  posts: PostsReducer,
  post: PostReducer, // our new reducer
    comments: CommentReducer,
  categories: CategoriesReducer,
});

基本上一个人会处理多个 post 对象形状,一个人会处理一个 post 对象形状。

现在,您唯一需要更改的是 Post_DetailedPost.js 组件中的 mapStateToProps
它将使用 post 减速器,而不是使用 posts 减速器:

function mapStateToProps(state) {
    return {post: state.post}
}  

这应该可以解决您的问题。

我认为问题是这样的:投票操作的 reducer 逻辑假设状态有一些特定的结构(一个具有 posts 的对象,以它们的 id 作为键。),但是那么 getPost 操作的 reducer 逻辑打破了这个假设。

查看了 repo 中的代码,专门针对 "DetailedPost"。这看起来有点不对劲,但我可能错了:

// in reducer_posts.js
case POST_GET_POST:**strong text**
  return payload;

我认为您会从 API 中得到一个特定的 post。如果是这样,这个 return 基本上会摆脱状态中的所有其他内容。 (所有其他 posts)。也许这就是你想要的,但我从未见过 reducer 以这种方式改变状态的结构。我原以为它会像

case POST_GET_POST:
  return {...state, [payload.id]: payload}

(如果你真的想让 post 的其余部分消失,也许没有 ...state。)

但是您还必须为详细视图更改 mapStateToProps。您应该能够从第二个参数访问 Router 道具(注意:ownProps 上的确切路径可能不同。我不记得 url 参数是如何存储的)

// in Post_DetailedPost.js
function mapStateToProps(state, ownProps) {
    const idFromURL =  ownProps.id; // or maybe ownProps.match.params.id
    return {post: state.posts[idFromURL]};
}

这实际上很可能是问题所在,因为您给 post 投了赞成票,您得到了新的(更新的)post,然后您像这样更改了状态。

case SUBMIT_POST_VOTE:
            return {...state, [payload.id]: payload} 

但这不是您用来将 post 向下传递到您的组件的状态结构。因此,您不会将更新的道具传递给详细信息视图。 (但根据对 mapStateToProps 的建议编辑,它可能会起作用。)