Redux,React,MapStateToProps:从redux状态树中检索数组中的特定元素

时间:2018-03-12 16:45:35

标签: arrays reactjs redux

我在redux状态树中有一个名为“leagues”的reducer,它只是一个单独的联盟对象数组。每个联盟都有一个唯一的ID(在后端分配)和一个名字。

我正在尝试编写一个代表个人联盟的组件,所以我希望有一个mapStateToProps函数来检索正确的联赛。从url知道正确的联盟,即通过react-router-v4中的匹配prop。

我试过了:

function mapStateToProps(state, ownProps) {
    return {
        league: state.leagues.find(aLeague => aLeague.id === ownProps.match.params.id)
    }
}

但是这导致了一个错误“state.leagues.find”不是一个函数。

然后我试了

function mapStateToProps(state, ownProps) {
    return {
        league: state.leagues[ownProps.match.params.id]
    }
}

哪个没有错误,但检索错误的联盟 - 如果id为3,那么这将检索state.leagues [3],而不是state.leagues.where(league.id === 3)

当我尝试访问单个联盟的页面时,联赛的价值:

leagues: [
    {
      id: 54,
      name: 'Test League'
    },
    {
      id: 55,
      name: 'Another Test'
    }
  ],

联盟减速机代码:

const initialState = {
  leagues: []
};

export default (state = initialState, action = {}) => {
  switch(action.type) {
    case SET_USER_LEAGUES:
      return state = action.leagues
    case ADD_USER_LEAGUE:
    return [
      ...state,
      {
        id: action.league.id,
        name: action.league.name,
      }
    ];
    default: return state;
  }
}

非常感谢任何帮助。 谢谢!

5 个答案:

答案 0 :(得分:2)

这是因为当初始化redux存储时,你最有可能将初始状态设置为null,这就是为什么你得到错误state.leagues.find不是一个函数,一旦状态通过一个异步ajax调用然后状态就在那里。我建议在像componentDidMount这样的反应生命周期方法中制作这种逻辑,并在联盟状态可用后为联盟设置状态。像这样:

componentDidMount() {
  const { leagues } = this.state;
  if (leagues && leagues.length > 0) {
    // update the state to the correct league here and this.props.id is the id that you want
    const league = leagues.find(aLeague => aLeague.id === this.props.id);
    this.setState({
      league
    }
  }
}

我希望有所帮助。

答案 1 :(得分:1)

似乎在您的组件首次呈现时,其默认状态已设置为null,并且当您尝试在空对象上使用数组方法find时,该应用程序会崩溃。将联盟减速器的初始状态设置为空数组。

然后,如果您的数组为空,您的应用可能尚未检索到结果,并且您可以显示“正在加载...”这样的消息。

但是,这并不能解决数据库中实际有0个项目的问题。然后,即使有0条记录,你也会显示错误显示加载。

为此,我还建议添加一个isLoading reducer(默认状态为true),它在获取异步数据期间维护应用程序的状态。异步调用完成后,调度操作以更新appstate并将isLoading设置为false。只有这样你才能尝试从联盟减速器中检索值。

我还建议您使用另一个“联盟”减速器进行过滤,这样您就不必在组件中维护此逻辑。

答案 2 :(得分:0)

我发现IE中不支持Array.prototype.find。因此,可能存在浏览器兼容性问题。

您可以随时使用Array.prototype.filter(假设state.leagues始终为Array

function mapStateToProps(state, ownProps) {
    const { leagues = [] } = state;
    return {
        league: leagues.filter(aLeague => aLeague.id === ownProps.match.params.id)[0]
    }
} 

答案 3 :(得分:0)

你的减速机对我来说不对劲。你能试试这个:

export default (state = initialState, action = {}) => {
    switch (action.type) {
        case SET_USER_LEAGUES:
            return {
                ...state,
                leagues: action.leagues,
            };
        case ADD_USER_LEAGUE:
            const leagues = [...state.leagues, { id: action.league.id, name: action.league.name, }];
            return {
                ...state,
                leagues,
            };
        default:
            return state;
    }
}

你的一些函数正在操纵状态,并在返回bool,一个对象和一个数组之间进行更改。

答案 4 :(得分:0)

要解决此问题,我现在使用对象而不是数组。 (原因请参见下面的参考文献1)。在加载商店状态后渲染组件,以便可以使用mapStateToProps。我从工作代码中提取了一些代码摘录。希望还提供了一些有关在此用例中如何使用Redux reducer的技巧。请原谅任何错别字,我在此处内联编辑了代码摘录。

商店

import { createStore, applyMiddleware, combineReducers } from 'redux'
import thunkMiddleware from 'redux-thunk'
import * as reducers from './reducers'
const initialState = {
  items: {
    /* id(1): {id: PropTypes.number.isRequired, name: PropTypes.string}
       id(n): {id: PropTypes.number.isRequired, name: PropTypes.string} */
  }
}
var rootReducer = combineReducers(reducers)
window.store = createStore(rootReducer, initialState, applyMiddleware(thunkMiddleware))

动作

export const UPDATE_ITEM = 'UPDATE_ITEM'
export const updateItem = item => ({
  type: UPDATE_ITEM,
  item
})

减速器

import { UPDATE_ITEM } from './actions'
export const items = (state = null, action) => {
  switch (action.type) {
    case UPDATE_ITEM:
      let id = action.item.id
      return Object.assign({}, state, {
        [id]: Object.assign({}, state[id], action.item)
      })
    default:
      return state
  }
}

向商店添加一些对象

import { updateItem } from './actions'
var item1 = {id: 1, name: 'Alice'}
window.store.dispatch(updateItem(item1))
var item2 = {id: 2, name: 'Bob'}
window.store.dispatch(updateItem(item2))

SomeComponent mapStateToProps

function mapStateToProps (state, ownProps) {
  return {
    item: state.items[ownProps.id]
  }
}

在商店填充后加载这样的组件。

ReactDOM.render(
  <Provider store={window.store}>
    <SomeComponent id={1} />
    <SomeComponent id={2} />
  </Provider>,
  document.getElementById('root')
)

仅需注意,解决此问题的主要动机是我将整个对象状态(state.items)映射到props,但随后在更新了任何对性能有害的数组元素之后调用了render。 / p>

参考文献:

[1] https://stackoverflow.com/a/36031765/1550587