【问题标题】:Component not updating when redux store modified修改 redux 存储时组件未更新
【发布时间】:2018-08-10 09:36:57
【问题描述】:

我正在使用 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. 我正在更新状态,而不是改变它

我很失望 Redux 在这种情况下并没有“正常工作”,尽管我可能在只睡了几个小时的情况下忽略了一些愚蠢的事情!任何帮助表示赞赏。

【问题讨论】:

  • 你的状态树改变了吗?如果您也可以将正在使用该状态对象的组件发布在其中,那就更好了。
  • 请向我们展示您的组件实现以及您连接到商店的部分。
  • 通过调用 props.dispatch 和 mapStateToProps 连接。状态树正在发生变化,但仅在页面加载时发生。
  • 给我看GET_ACTIVE_CAMPAIGNS reducer
  • @MattSaunders 是的,强制从另一个不负责这样做的组件进行刷新将是糟糕的设计。再次查看您的代码后,我想我知道您的问题是什么。当您切换网站时,您的 ActiveCampaignsDropdown 不知道它。因为它的所有道具都没有改变,所以没有更新。我将编辑我的答案,为您提供完整的解决方案。 PS:直接为组件提供dispatch函数也被认为是不好的做法。而是使用 mapDispatchToProps 为您的调度调用提供包装函数(请参阅下面的答案)

标签: reactjs redux redux-thunk


【解决方案1】:

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

  • 道具已更改
  • 状态已更改
  • forceUpdate() 被调用

根据您的情况,您希望在商店中的state.activeCampaigns 发生更改时更新ActiveCampaignsDropdown。为此,您必须连接您的组件,以便它接收此值作为道具(并因此在它更改时强制更新)。

这可以按如下方式完成:

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。正如我的一个 cmets 中所提到的,“强制”从不负责这样做的组件中进行这种更改将被视为不好的做法。

一个完全合理的解决方案是让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())——顺便说一句,这也应该尽可能避免。

【讨论】:

  • 非常感谢。现在,如果我对所有操作调用都使用 mapDispatchToProps,我该如何传递参数?例如,如果此调用在 mapDispatchToProps 中,我将如何将参数传递给它:setSelectedCampaign: () => dispatch(setSelectedCampaign())。希望不要使用内部状态!
  • 我已经通过像这样传递值来解决这个问题: setSelectedCampaign: (campaignId) => dispatch(setSelectedCampaign(campaignId))
  • @MattSaunders 是的,你明白了
猜你喜欢
  • 2019-09-23
  • 1970-01-01
  • 2020-01-27
  • 2021-02-24
  • 2020-08-30
  • 1970-01-01
  • 2018-10-15
  • 2017-10-26
相关资源
最近更新 更多