web-dev-qa-db-ja.com

Reduxストアが変更されたときにコンポーネントが更新されない

私はアクションでAPI呼び出しを返すためにredux thunkを使用しています:

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アクション。これは、ユーザーがドロップダウンリストからWebサイトを選択するアプリの他の場所のアクションから設定されます。私の減速機は次のようになります:

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;
    }
}

選択したWebサイトを設定するための私のアクション:

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

これは次のように他のレデューサーと組み合わされます:

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

問題

アクティブなキャンペーンのドロップダウンボックスのコンテンツはページの読み込み時に正常に機能し、状態ツリーは更新されますが、選択したWebサイトが変更されても更新されません 。私が見ることができるものから:

  1. アクションを正しくディスパッチしています
  2. 状態を変更するのではなく、更新しています

Reduxがこのインスタンスで「正常に動作している」わけではないことにかなりがっかりしています。ただし、数時間しか眠っていなかった愚かなことを見落としている可能性もあります。助けてくれてありがとう。

5
Matt Saunders

Reactでは、コンポーネントは次の3つのいずれかが発生すると更新されます。

  • 小道具が変更されました
  • 状態が変更されました
  • 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が正常に更新されます。私のコメントの1つで述べたように、責任を負わないコンポーネントからこの変更を「強制」することは、悪い習慣と見なされます。

完全に合理的な解決策は、現在のWebサイトへの変更をActiveCampaignsDropdownが「リッスン」し、それに応じて更新することです。このためには、2つのことを行う必要があります。

(1)Webサイトの状態をコンポーネントにマッピングする

_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();
        }
    }
}
_

これで、選択したWebサイトが変更されるたびに更新が行われ、componentWillReceiveProps()が呼び出されます(activeCampaignsも更新する)。この更新が適用されると、別の更新が行われ、レンダリングされたドロップダウンに新しく更新されたキャンペーンが含まれます。

いくつかのマイナーな改善:

  • 多くのコンポーネントが現在のWebサイトの状態に依存している場合(私はそれが多いと思います)、 context を使用してコンポーネントを提供することを検討できます。

  • ActiveCampaignsDropdownが 'selectedWebsite'を小道具として受け取ったので、これを状態からフェッチするのではなく(getState()を使用して)アクション関数に直接渡すことができます。また、可能な場合は避けてください。

2
sookie

レデューサーに保存しているときにスプレッド演算子を使用します。

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