如果连接到未更改的商店,React componentDidUpdate 方法不会在继承的道具更改时触发

React componentDidUpdate method won't fire on inherited props change if connected to a store that didn't change

我想让我的组件知道是否已经加载了一些库。要知道从任何上下文我都将它连接到我商店的 "library" reducer 到我的组件。 我还从调用组件的父级向它传递一个配置对象 this.props.dataObject。像这样:

class GoogleButton extends Component {
    render() {
        if (this.props.libraries.google) {
            return <a id='sharePost' className='google_icon'></a>
        } else {
            return null
        }
    }

    componentDidUpdate() {
        gapi.interactivepost.render('sharePost', this.props.dataObject)
    }
}

function mapStateToProps(state) {
    return { libraries: state.libraries }
}

export default connect(mapStateToProps)(GoogleButton)

处理库状态的reducer是这样的:

let newState = {...state}
newState[action.libraryName] = action.state
return newState 

当我更改库状态时 componentDidUpdate 有效。问题是当我更改父 this.props.dataObject 继承的道具时。在那种情况下,componentDidUpdate 不会触发。如果我从组件中删除 connect 它会按预期工作。我在这里遗漏了什么?

很可能你的一些道具在组件外发生了变化。
例如,您可能会像这样渲染您的组件:

class Parent extends Component {
  constructor() {
    super()
    this.state = { libraries: {} }
  }

  handleClick() {
    // MUTATION!
    this.state.libraries.google = true

    // Normally this forces to update component anyway,
    // but React Redux will assume you never mutate
    // for performance reasons.

    this.setState({ libraries: this.state.libraries })
  }

  render() {
    return (
      <div onClick={() => this.handleClick()}>
        <GoogleButton libraries={this.state.libraries} />
      </div>
    )
  }
}

因为 Redux 应用程序处理不可变数据,connect() 使用 shallow equality check 作为其 props 以避免不必要的重新渲染。但是,如果您在应用中使用突变,这将不起作用。

你有两个选择:

不要改变任何东西

这是最好的选择。例如,而不是像

  handleClick() {
    this.state.libraries.google = true
    this.setState({ libraries: this.state.libraries })
  }

你可以写

  handleClick() {
    this.setState({
      libraries: {
        ...this.state.libraries,
        google: true
      }
    })
  }

这样我们就创建了一个新对象,这样 connect() 就不会忽略更改后的引用。 (我在此片段中使用 object spread syntax。)

禁用性能优化

一个更糟糕的选择是完全禁用 connect() 所做的性能优化。然后,即使您在父级中改变它们,您的道具也会更新,但您的应用程序会变慢。为此,替换

export default connect(mapStateToProps)(GoogleButton)

export default connect(mapStateToProps, null, null, { pure: false })(GoogleButton)

除非绝对必要,否则不要这样做。

我解决了。我不是 100% 确定这是准确的,但我会解释。如果我有什么不对的地方,请指正。

我一直在想 Dan 在他的回答中所说的 shallow equality check。问题就在那里。 我从父级传递一个对象,该对象的嵌套元素发生了变化。对象保持不变。因此,通过 connect 带来的浅层相等性检查,组件将永远不会更新。

我的解决方案是在传递道具时让父级使用 Object.assign({}, dataObject),因此我制作了另一个不同的对象。现在浅层相等检查可以比较它并确定 props 已经改变并且在更新组件之前。

我有同样的问题,我使用 object.assign 创建新状态,但我使用 combineReducer 并导致多级状态,在我的情况下,我将整个状态作为道具传递给组件,所以 shallow equality check 可以没有检测到我的状态变化,所以 componentDidUpdate 没有调用,重要的是在使用 combine reducer 时在它改变的级别传递状态 就我而言,我是这样传递的

const MapStateToProps=(state)=>{
    return {
        reportConfig:state.ReportReducer
    }
};

我的状态树是这样的

  {
  ReportReducer: {
    reportConfig: {
      reportDateFilter: 'this week',
      reportType: null,
      reportShopId: null,
      updateShop: true
    }
  }
}

在我的减速器中 return 它像这样 ReportReducer

export default combineReducers({reportConfig});

我的root reducer是这样的

const rootReducer =combineReducers({ReportReducer});

const store = createStore(rootReducer ,{},enhancer);

您可以使用的另一个选项是在子组件上制作继承道具 this.props.dataObject 的深层副本,这是为了 componentDidUpdate 到 'catch' 更新的道具,您可以使用:

dataObject={JSON.parse(JSON.stringify(valueToPass))}

在从父组件传递 prop 的地方使用它,这对我有类似的问题(当你在 prop 中没有任何功能时这适用)。

我在使用外部库中的组件时遇到了完全相同的问题。 所以我没有修改继承的 属性.

的选项

我只需要一部分继承的 属性 对象(为简单起见,将使用 dataObject)。通过将它添加到 mapStateToProps 函数来解决它:

function mapStateToProps(state, ownProps) {
    return { libraries: state.libraries, neededValue: ownProps.dataObject.value }
}

浅比较足以注意到值的变化。所以在render()函数中使用this.props.neededValueisothis.props.dataObject.value