【问题标题】:React component not updating when store state has changed商店状态更改时反应组件不更新
【发布时间】:2016-06-25 08:09:39
【问题描述】:

下面是我的组件类。该组件似乎永远不会执行 componentWillUpdate(),即使我可以在 mapStateToProps 返回之前通过记录看到状态更新。状态 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
  }
}

经过更多的测试和记录后,似乎当它去映射状态到道具时,它用来映射到道具的状态对象包含正确的数据,所以 state.map.data 是正确的,我可以看到从拿来。但是,当我在 componentWillUpdate() 中记录 this.props 时,数据对象在那里但为空。

【问题讨论】:

  • 你能展示你的减速器吗?可能是你改变了状态而不是发送新对象,所以 redux 认为没有理由更新。
  • render 中没有任何内容:&lt;div id="map"&gt;&lt;/div&gt;
  • @DavinTryon 我正在使用 Leaflet 来处理它自己的渲染,但它仍然应该像在更新组件一样。
  • @Mijamo 我已经添加了,谢谢。

标签: javascript reactjs state redux


【解决方案1】:

以玛丽娜的回答为基础

var tempArray = imageArrayState
tempArray.push(5)
setImageArray(tempArray)

var tempArray = []
imageArrayState.foreach(value => {tempArray.push(value)})
tempArray.push(5)
setImageArray(tempArray)

让我的应用刷新

【讨论】:

    【解决方案2】:

    因为你没有改变引用,所以 React 的浅比较没有检测到更新。

    我将使用一个简单的博客文章示例。在您的减速器中,您可能正在执行以下操作:

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

    除此之外,您必须执行以下操作:

    case FETCH_NEW_POSTS
        let posts = [...state.posts]; // we're destructuring `state.posts` inside of array, essentially assigning the elements to a new array.
        posts.push(action.payload.posts);
        return {
            ...state, 
            posts
        };
    

    根据您的用例,Object.assign() 或 lodash 的 clone/deepclone 可能更惯用。

    【讨论】:

    • 这个。谢谢,这正是问题所在,应该是最佳答案
    • 只是为了改写它,在第一种情况下,您将推送到一个数组,该数组会修改它,但该数组在内存中保持相同的引用。因此,当您返回帖子时,您仍然返回相同的数组,因此状态不会更新。在第二种情况下,您正在创建数组的副本。
    • 这个解决方案应该是最重要的,它是完美的解决方案
    • 这正是我试图解决一天的问题。帮了我很多;)
    • @dillon.harless,不,在 JS 对象中是引用类型。在第一个块中,您将posts 设置为与state.posts 相同的引用。由于 React 的浅比较注意到相同的对象指针,因此它不会被更新。在此处查看更多信息:developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/… 特别是底部的“比较对象”部分。
    【解决方案3】:

    我也遇到过类似的问题,看完这篇我找到了答案:

    数据通过 reducer 中的处理结果在 store 中设置/更新/删除。 Reducers 接收应用程序切片的当前状态,并期望恢复新状态。您的组件可能不会重新渲染的最常见原因之一是您正在修改 reducer 中的现有状态,而不是返回具有必要更改的新状态副本(查看故障排除部分)。当您直接改变现有状态时,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]

    希望对你有帮助

    【讨论】:

    • 我尝试使用Lodash的克隆功能,但没有成功。然而,使用{...object} 却成功了。
    • var tempArray = imageArrayState tempArray.push(5) setImageArray(tempArray)
    • 这应该是公认的答案
    【解决方案4】:

    如果您通过地图文件将道具传递给组件,请确保您正在收听地图文件中的存储。

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

    【讨论】:

    • 看来这可能是我的方案的答案,但connection 函数是从哪里导入的?
    【解决方案5】:

    componentWillUpdate 接收传入的道具作为参数。这时候this.props还是老道具了。尝试像这样更改您的方法:

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

    【讨论】:

    • 嗨,布兰登,感谢您的回复。该函数似乎仍然没有被调用。在我的三个操作中,请求、接收和获取,我已经登录到控制台,这一切似乎都很好。我还登录了 reducer,状态肯定会发生变化。
    • 您需要显示您的操作的代码以及调度这些操作的代码。
    • 嗨,布兰登,刚刚发现一些奇怪的东西可能会有所帮助。检查问题以获取更多详细信息。
    • 啊,我想我解决了......似乎数据在 componentDidUpdate() 期间存在,但不是 componentWillUpdate()。更新后的数据不是没有意义吗?您确定需要预先更新新数据吗?
    猜你喜欢
    • 1970-01-01
    • 2018-09-01
    • 1970-01-01
    • 2021-10-08
    • 1970-01-01
    • 2017-05-31
    • 1970-01-01
    • 2022-06-21
    • 2021-07-07
    相关资源
    最近更新 更多