简体   繁体   English

尽管props中的Object发生变化,但React组件未重新渲染

[英]React component not re-rendering, although Object in props changes

I know, there are many, many similary questions.. ** duplicate alarm! 我知道,有很多很多相似的问题。** 重复警报! ** **

But: I looked through all of them, I promise. 但是:我保证,我会仔细检查所有这些对象。 I'm quite sure now, that this is another case, that could have to do with the props being an object (from what I've read here). 我现在很确定,这是另一种情况,可能与道具成为物体有关(从我在这里阅读的内容)。 But I couldn't solve the following, anyway: 但无论如何,我无法解决以下问题:

class CsvListDropdown extends Component {
    constructor(props) {
        super(props);
        this.state = { sessions: props.sessions }
        this csvsInSession = this.csvsInSession.bind(this);
    }

    csvsInSession(sessions) {
        return (sessions
            .map(keys => Object.entries(keys)[2][1])
            .map((csv, i) => (
                <option value={csv} key={i}>{csv}</option>
            ))
        )
    }

    render() {
        const { isLoading } = this.props
        if (isLoading) { blablabla.. }
        else {
            return (
                ...
                <select value={this.props.sessions[0].currentCsv}>
                    {this.csvsInSession(this.state.sessions)}
                </select>
                ...
            )
        }
    }
}

export default withTracker(() => {
    const handle = Meteor.subscribe('sessions');
    return {
      isLoading: !handle.ready(),
      sessions: Sessions.find({}).fetch() 
    };
})(CsvListDropdown);

Now from the client I am writing another document into the Sessions collection, containing the .csv filename, while this new csv file is being uploaded to a remote server. 现在,从客户端,我将另一个文档写入Sessions集合,其中包含.csv文件名,而此新的csv文件正在上传到远程服务器。 console.log(this.props.sessions) gives me an array, which is up to date. console.log(this.props.sessions)给了我一个数组,它是最新的。 But the component itself does not re-render. 但是组件本身不会重新渲染。

What I also don't understand is: console.log(this.state.sessions) returns undefined . 我还不明白的是: console.log(this.state.sessions)返回undefined (note: state ) (注: 状态

What I tried so far: 到目前为止我尝试过的是:

  • {this.csvsInSession(this.props.sessions)} (note: props ) {this.csvsInSession(this.props.sessions)} (注意: props
  • Adding a withTracker / State / Props to the parent component and passing the sessions object from either state or props as params to the child component, that should re-render. withTracker / State / Props添加到父组件,并将sessions对象从stateprops作为参数传递给子组件,然后应重新呈现。

  • forceUpdate() 强制性升级()

  • componentWillUpdate() componentWillUpdate()

What may be important as well: The component should re-render about the same time another component also re-renders (which displays the contents of uploaded CSVs, that return from a microservice and get written into another collection). 可能也很重要:该组件应该在另一个组件也重新渲染的同时重新渲染(显示从微服务返回并写入另一个集合的已上传CSV的内容)。 The latter does actually re-render.. But that dropdown does not.. argh! 后者实际上确实会重新渲染。但是该下拉列表却没有。

this.state will only change if you call this.setState() , which you are not doing. 仅当您调用this.setState()而不执行时, this.state才会更改。 You are initializing state with a value from props , but only in the constructor when the component is first instantiated. 您将使用props的值初始化state ,但仅在首次实例化组件时才在构造函数中进行初始化。 After that, even if props changes your component may re-render but what it displays won't change because state hasn't been updated. 此后,即使props更改,您的组件也可能会重新渲染,但其显示的内容不会更改,因为state尚未更新。

In fact, there does not appear to be any reason whatsoever to store data in state in that component. 实际上,似乎没有任何理由在该组件中以状态存储数据。 It might as well be a functional presentational component: 它也可能是功能性的演示组件:

function CsvListDropdown(props) {
    function csvsInSession(sessions) {
        return (sessions
            .map(keys => Object.entries(keys)[2][1])
            .map((csv, i) => (
                <option value={csv} key={i}>{csv}</option>
            ))
        )
    }

    const { isLoading } = props;
    if (isLoading) { blablabla.. }
    else {
        return (
            ...
            <select>
                {csvsInSession(props.sessions)}
            <select>
            ...
        )
    }
}

Generally all of your components should be stateless functional components unless they specifically need to store internal state for some reason. 通常,除非出于某种原因特别需要存储内部状态,否则所有组件都应为无状态功能组件

Now I finally solved it, and it turns out that the component did actually update at any time, but I did not notice it, simply because the latest item in the array was quietly appended to the bottom of the dropdown list. 现在我终于解决了它,而且事实证明,该组件实际上确实随时更新,但我没有注意到它,只是因为数组中的最新项目是悄悄地追加到下拉列表的底部 This however I was not expecting, as I had published the collection with a descending sorting . 但是,这并不是我所期望的,因为我以降序排序 发布了该系列。

// server-side
Meteor.publish('sessions', function() {
    return Sessions.find({ userId: this.userId }, { sort: {createdAt: -1} });
});

Server-side seems to be the wrong place to sort. 服务器端似乎是错误的排序位置。 It simply does not have an effect. 它根本没有作用。 So sorted on the client side, when subscribing: 因此,在订阅时在客户端排序:

// client-side
export default withTracker(() => {
    const handle = Meteor.subscribe('sessions');
    return {
      isLoading: !handle.ready(),
      sessions: Sessions.find({}, { sort: {createdAt: -1} }).fetch() 
    };
})(App)

I had omitted an important detail from my question, that is how I set the value of the dropdown field: 我已经从问题中省略了一个重要的细节,那就是如何设置下拉字段的

<select value={this.props.sessions[0].currentCsv}>
    {this.csvsInSession(sessions)}
</select>

So lesson learned: If you think your react component does not re-render, always check if that's true, before assuming so. 因此,我们汲取了以下教训:如果您认为自己的react组件没有重新渲染,请在进行假设之前始终检查是否正确。


As a side effect of debugging I restructered my components. 作为调试的副作用,我重构了我的组件。 Now the Meteor.subscribe() is within the parent component, that contains all the children, that have to handle the sessions object. 现在, Meteor.subscribe()位于父组件内,该组件包含必须处理sessions对象的所有子代。 And the sessions object gets passed down from the parent to the (grand)children as props. 然后sessions对象作为道具从父级传递到(大)子级。 I think it's more readable and easier to maintain that way. 我认为这种方式更具可读性,并且更容易维护。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM