私はアクションで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サイトが変更されても更新されません 。私が見ることができるものから:
Reduxがこのインスタンスで「正常に動作している」わけではないことにかなりがっかりしています。ただし、数時間しか眠っていなかった愚かなことを見落としている可能性もあります。助けてくれてありがとう。
Reactでは、コンポーネントは次の3つのいずれかが発生すると更新されます。
あなたの状況では、ストアで_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()
を使用して)アクション関数に直接渡すことができます。また、可能な場合は避けてください。
レデューサーに保存しているときにスプレッド演算子を使用します。
return {
cart: [...cart]
};