如果连接到没有改变的商店,则React componentDidUpdate方法不会触发继承的道具更改

时间:2016-06-03 21:22:29

标签: javascript reactjs redux react-redux

我希望我的组件知道是否已加载某个库。要知道从任何上下文我连接到"库"我商店的减速机到我的组件。 我还从已调用组件的父级传递配置对象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,它会按预期工作。我在这里错过了什么?

5 个答案:

答案 0 :(得分:8)

很可能你的一些道具在组件外突变 例如,您可能正在渲染组件:

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作为其道具以避免不必要的重新渲染。但是,如果您在应用中使用变异,这将无效。

您有两种选择:

不要突变

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

那样的东西
  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)

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

答案 1 :(得分:3)

我解决了。我不是100%确定这是准确的,但我会解释。如果我的错误,请纠正我。

我一直在想丹在他的回答中所说的shallow equality check。问题出在那里。 我从父对象传递了一个对象,该对象的嵌套元素是那些已经改变的对象。对象保持不变。因此,使用浅层相等检查connect使组件永远不会更新。

当我传递道具时,我的解决方案在父使用Object.assign({}, dataObject)中,因此我创建了另一个不同的对象。现在浅层等式检查可以比较它并确定道具已经改变,并在更新组件之前进行更改。

答案 2 :(得分:0)

我遇到了同样的问题,我使用object.assign创建了新状态,但是我使用CombineReducer并导致了多级状态,就我而言,我将整个状态作为prop传递给了组件,因此shallow equality check无法检测到我的状态进行更改,以便componentDidUpdate没有调用,在使用Combine Reducer时在更改级别传递状态非常重要 就我而言,我像这样通过它

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

我的状态树就是这样

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

,然后在我的减速器中,将其返回为ReportReducer

export default combineReducers({reportConfig});

我的减根剂是这样的

const rootReducer =combineReducers({ReportReducer});

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

答案 3 :(得分:0)

您可以使用的另一种选择是在子组件上制作继承道具this.props.dataObject的深层副本,这是为了使componentDidUpdate能够“捕获”更新的道具,您可以使用:

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

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

答案 4 :(得分:0)

我在外部库中使用的组件也遇到了同样的问题。 所以我没有选择修改继承的属性。

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

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

通过一个浅比较就足以注意到值的变化。因此,请在this.props.neededValue函数中使用this.props.dataObject.value iso render()