从API获取的Redux Observable,React,componentDidMount无法从对象获取属性

时间:2017-08-20 17:55:41

标签: reactjs redux rxjs redux-observable

    displayPosts () {
        //console.log(post) works, console.log(post.title) returns undefined.
        return  _.map(this.props.posts, (post)=>{debugger;console.log(post.title);});
    }

我可以在

中执行console.log(post)
    displayPosts () {
        //console.log(post) works, console.log(post.title) returns undefined.
        return  _.map(this.props.posts, (post)=>{return <div>post.title</div>;});
    }

但是如果我尝试console.log(post.title),我会得到未定义的。

同样,如果我尝试做类似的事情:

select * 
from TableABC 
where TableABC.label_id is null

union all

select * 
from TableABC 
join Labels 
  on TableABC.label_id = Lables.id 
 and Lablestype = '123'

我一无所获。

在这里你可以看到console.log()的结果。 前者来自史诗,后者来自组成部分。

Console logs

这里有我打开的回购链接的链接: https://github.com/Deviad/redux-router-playground

1 个答案:

答案 0 :(得分:4)

问题实际上在你的reducer中(从你的链接回购中找到):

export default function postsReducer(state=[], action) {
    switch (action.type) {
        case ActionTypes.FETCH_POSTS_FULLFILLED:
            // THIS IS THE PROBLEM:
            return [
                ...state,
                action.payload
            ];

        default:
            return state;
    }
};

action.payload已经是一个数组,但是你把它放在另一个数组中,所以是一个数组数组。因此,当您映射this.props.posts时,您只得到1个结果,即实际的帖子数组。当你记录时,你可能只是没注意到。

// THIS IS THE PROBLEM:
return [
    ...state,
    action.payload
];

相反,你可以按原样返回数组:

return action.payload;

虽然上述解决方案是IMO可接受的(并且说实话,运输代码是第一优先级),但这实际上仍然不是使用redux的最惯用的方式。相反,将redux视为数据库。你如何在数据库中存储(也正常化)这些?如果您回答“按ID编制索引”,那么您是对的!

所以你的redux状态看起来像这样:

{
    posts: {
        '123': {
            id: '123',
            title: 'first title'
        },
        '456': {
            id: '456',
            title: 'second title'
        }
    }
}

这是你可以做到的一种方式:

export default function postsReducer(state = {}, action) {
    switch (action.type) {
        case ActionTypes.FETCH_POSTS_FULLFILLED:
            return action.payload.reduce((acc, post) => {
                acc[post.id] = post;
                return acc;
            }, { ...state });
            // use Object.assign if object-spread
            // syntax isn't supported

        default:
            return state;
    }
};

因为这种情况的变化很常见,很多时候是嵌套的,有些人会选择使用normalizr库:

import { normalize, schema } from 'normalizr';

const postSchema = new schema.Entity('posts');

export default function postsReducer(state = {}, action) {
    switch (action.type) {
        case ActionTypes.FETCH_POSTS_FULLFILLED:
            // use Object.assign if object-spread
            // syntax isn't supported
            return {
                ...state,
                ...normalize(action.payload, [postSchema]).entities.posts
            };
// etc

因为在您的情况下,您需要将它们作为该UI容器中的帖子数组返回,它也具有用于非规范化的实用程序,或者如果支持,您可以使用Object.values执行此操作:

function mapStateToProps(state) {
    return {
        posts: Object.values(state.posts)
    };
}

normalize()调用还会返回一个ID数组,您可能会发现它们可以在某个地方存储在redux中,或者您可以在需要时按需使用Object.keys(state.posts) - 这就是我所说的因为它可以防止同步问题,但如果项目数量很大,那么对于性能原因并不总是可行的。这是一个我担心的问题。

你可能想知道为什么我们会在那个问题上再次对它进行反规范化呢?完整的答案是长篇大论,但简短的要点是一致性,未来状态更新的简易性,以及后续视图的快速“发布ID”查找。这是使redux变得更好的一个主要部分,否则它变成了一个时间旅行的美化吸气剂/定位器,它几乎不值得借助它。

这在redux文档的Normalizing State Shape部分进行了讨论。