修改redux store时组件不更新

时间:2018-03-02 07:36:20

标签: reactjs redux redux-thunk

我使用redux thunk在动作上返回API调用:

export const getActiveCampaigns = () => {
return (dispatch, getState) => {
    const bearer = 'Bearer ' + getState().login.bearer
    return axios.get(API.path + 'campaign/active/?website_id=' + getState().selectedWebsite.selectedWebsite + '&' + API.beaconAPI_client, { headers: { 'Authorization': bearer } })
    .then(function (response) {
        dispatch({
            type: GET_ACTIVE_CAMPAIGNS,
            activeCampaigns: response.data.response
        })
    })
  }
}

这样可以成功地返回一个广告系列列表,我将使用以下内容将其渲染到另一个组件中:

class ActiveCampaignsDropdown extends Component {
    // usual stuff

    componentDidMount(){
        this.props.dispatch(getActiveCampaigns())
    }

    // render function displays campaigns using this.props.activeCampaigns
}

const mapStateToProps = (state) => {
    return {
        activeCampaigns: state.activeCampaigns.activeCampaigns
    }
}

但是,请注意该操作的getState.selectedWebsite.selectedWebsite。这是通过应用程序中其他位置的操作设置的,用户从下拉列表中选择网站。我的减速器看起来像这样:

export default function (state = {}, action) {
switch(action.type){
    case SET_SELECTED_WEBSITE:
        return {
            ...state,
            selectedWebsite: action.websiteId
        }
    default:
        return state;
  }
}

export default function (state = {}, action) {
    switch(action.type){
        case GET_ACTIVE_CAMPAIGNS:
        return {
            ...state,
            activeCampaigns: action.activeCampaigns
        }
        default:
        return state;
    }
}

我设置所选网站的操作:

export const setSelectedWebsite = (websiteId) => {
    return {
        type: SET_SELECTED_WEBSITE,
        websiteId
    }
}

这与其他减速器结合使用:

export default combineReducers({
    login,
    activeWebsites,
    activeCampaigns,
    selectedWebsite  
})

问题

活动广告系列下拉框的内容在页面加载时工作正常 - 状态树确实更新 - 但在所选网站更改时不会更新。从我所看到的:

  1. 我正在正确派遣行动
  2. 我正在更新状态,而不是改变状态
  3. 我非常失望Redux不是“只是工作”而且#34;在这种情况下,虽然有可能我只是睡了几个小时才能看到傻事!任何帮助表示赞赏。

2 个答案:

答案 0 :(得分:2)

在React中,当发生以下三种情况之一时,组件会更新:

  • 道具改变了
  • 状态已更改
  • 强制调用forceUpdate()

在您的情况下,您希望在商店中发生ActiveCampaignsDropdown更改时更新state.activeCampaigns。要做到这一点,你必须连接你的组件,使它作为道具接收这个值(并因此在更改时强制更新)。

这可以按如下方式完成:

import {connect} from 'react-redux'

class ActiveCampaignsDropdown extends React.Component { ... }
const mapStateToProps = (state) => ({activeCampaigns: state.activeCampaigns});
const Container = connect(mapStateToProps)(ActiveCampaignsDropdown);

export default Container; 

最终Container组件将通过其道具完成将ActiveCampaignsDropdown与所需商店状态相关联的所有工作。

Redux的connect()还允许我们连接调度函数来修改商店中的数据。例如:

// ... component declaration
// ... mapStateToProps

const mapDispatchToProps = (dispatch) => 
{
    return {
        getActiveCampaigns: () => dispatch(getActiveCampaigns())
    };
}

const Container = connect(mapStateToProps, mapDispatchToProps)(ActiveCampaignsDropdown);

一旦定义了映射函数,就会创建容器组件,并呈现容器,ActiveCampaignsDropdown将被正确连接。在我的示例中,它将接收'activeCampaigns'和'getActiveCampaigns'作为道具,并在其值发生变化时进行相应更新。

修改:

再看一下您的代码后,我认为您的问题是由于在网站发生变化时更新ActiveCampaignsDropdown没有满足任何条件的事实。通过从您的WebsiteDropdown调用getActiveCampaigns()(根据您的评论),这会强制state.activeCampaigns更改,从而成功更新ActiveCampaignsDropdown。正如我的一条评论中提到的那样,“强迫”这个改变来自一个不负责任的组件,这将被视为不良做法。

一个完全合理的解决方案是ActiveCampaignsDropdown“监听”当前网站的更改并相应地更新自己。为此,您需要做两件事:

(1)将网站状态映射到组件

const mapStateToProps = (state) => {
    return {
        activeCampaigns: state.activeCampaigns.activeCampaigns, // unsure why structured like this
        selectedWebsite: state.selectedWebsite.selectedWebsite
    }
}

(2)将您的调度调用移至componentWillReceiveProps

class ActiveCampaignsDropdown extends React.Component
{
    // ...

    componentWillReceiveProps(nextProps)
    {
        if (this.props.selectedWebsite !== nextProps.selectedWebsite)
        {
            this.props.getActiveCampaigns();
        }
    }
}

现在每次所选网站发生更改时,都会进行刷新并调用componentWillReceiveProps()(导致activeCampaigns也更新)。应用此更新后,将进行另一次刷新,呈现的下拉列表将包含新更新的广告系列。

一些小改进:

  • 如果您的许多组件依赖于当前网站的状态(我想象的很多),那么您可以考虑通过context向他们提供。

  • 现在您的ActiveCampaignsDropdown收到'selectedWebsite'作为道具,您可以直接将其传递给您的动作函数,而不是让它从状态(使用getState())获取 - 通过如果可能的话,也应该避免这种方式。

答案 1 :(得分:0)

在减速器中存储时使用扩展运算符:

return  {
    cart: [...cart]
};