web-dev-qa-db-ja.com

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

以下は私のコンポーネントクラスです。 mapStateToPropsに戻る前にログに記録することで状態の更新を確認できる場合でも、コンポーネントがcomponentWillUpdate()を実行するようには見えません。状態は100%変化していますが、コンポーネントは更新されません。

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { search } from './mapActions'
import L from 'leaflet'


class Map extends Component {
  componentDidMount() {
    L.Icon.Default.imagePath = './images'
    this.map = new L.Map('map', {
      center: new L.LatLng(this.props.lat, this.props.lng),
      zoom: this.props.zoom,
      layers: L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
        attribution: '<a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
      })
    })
  }
  componentWillUpdate() {
    console.log('UPDATE MAP')
    L.geoJson(this.props.data).addTo(this.map)
  }
  render() {
    return <div id="map"></div>
  }
}

const mapStateToProps = (state) => {
  return {
    isFetching: state.isFetching,
    data: state.data
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    search: (name) => {
      dispatch(search(name))
    }
  }
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Map)

そしてこれがマップリデューサーです:

const initialState = {
  isFetching: false,
  data: {}
}

export const map = (state = initialState, action) => {
  switch(action.type) {
    case 'REQUEST_SEARCH_RESULTS':
      return Object.assign({}, state, {
        isFetching: true
      })
    case 'RECEIVE_SEARCH_RESULTS':
      return Object.assign({}, state, {
        isFetching: false,
        data: action.data
      })
    default:
      return state
  }
}

いくつかのテストとロギングを行った後、propsにマップするために使用する状態オブジェクトをpropsにマップすると、正しいデータが含まれているため、state.map.dataが正しく、フェッチからの戻りを確認できます。しかし、this.propsをcomponentWillUpdate()に記録すると、データオブジェクトは存在しますが空です。

11
Jacob Mason

私は同様の問題を抱えており、これを読んだ後に答えを見つけました:

データは、レデューサーでのアクションの処理結果を介して、ストアで設定/更新/削除されます。レデューサーは、アプリのスライスの現在の状態を受け取り、新しい状態を取り戻すことを期待します。コンポーネントが再レンダリングされない可能性がある最も一般的な理由の1つは、必要な変更を加えた状態の新しいコピーを返す代わりに、レデューサーの既存の状態を変更していることです(トラブルシューティングのセクションをご覧ください)。既存の状態を直接変更する場合、Reduxは状態の違いを検出せず、ストアが変更されたことをコンポーネントに通知しません。だから私は間違いなくあなたのレデューサーをチェックして、あなたが既存の状態を変更していないことを確認します。お役に立てば幸いです。 ( https://github.com/reactjs/redux/issues/585

私がObject.assign({}, object)をあなたのように使用しようとしたとき、それはとにかくうまくいきませんでした。だから私がこれを見つけたのは:

Object.assignは浅いコピーのみを作成します。 ( https://scotch.io/bar-talk/copying-objects-in-javascript

それから私はこれをしなければならないことを理解しました:JSON.parse(JSON.stringify(object))

またはこれだけ:{...object}

配列の場合:[...theArray]

これがあなたに役立つことを願っています

11
Marina

componentWillUpdate は、着信プロップを引数として受け取ります。現時点では、 this.propsはまだ古い小道具です。次のようにメソッドを変更してみてください:

void componentWillUpdate(nextProps, nextState) {
    L.geoJson(nextProps.data).addTo(this.map);
}
9
Brandon

私はマリーナと同様の問題を抱えていましたが、同様の方法で解決しました。うまくいけば、この説明は誰かを助けます。

ブログ投稿で簡単な例を使用します。あなたのレデューサーでは、おそらく次のようなことをしています:

case FETCH_NEW_POSTS
    let posts = state.posts;
    posts.Push(action.payload.posts);
    return {
        ...state, 
        posts
    };

その代わりに、次のことを行う必要があります。

case FETCH_NEW_POSTS
    let posts = [...state.posts];
    posts.Push(action.payload.posts);
    return {
        ...state, 
        posts
    };

最初の例では、奇妙なことに、実際の状態オブジェクトを設定しています。つまり、setStateと同等のメソッドを実行していないため、行った変更は取得されません。

5
NathanL

マップファイルを介してコンポーネントに小道具を渡す場合は、マップファイルでそのストアをリッスンしていることを確認してください。

export const listenToStores = [CommonStore, store];
@connection(maps.listenToStores, maps.getStateFromStores)
0

また、コンポーネントのコンストラクターを使用するときは注意してください。それが私の問題でした。

例えば.

class extends Component {
   private elementState: ElementState // BAD idea!!

   constructor(props: any) {
      super(props)
      elementState = stateFromReduxStore // BAD idea!!!
   }

   render() {
      return <SomeElement state={elementState} /> // will not be updated!!!
   }
}
0
crownlessking