【问题标题】:compare 2 arrays of objects, one with old and one with new data, then update state according to new data比较 2 个对象数组,一个与旧数据,一个与新数据,然后根据新数据更新状态
【发布时间】:2018-04-27 15:23:04
【问题描述】:

我有一个用户在这个表单的页面上有一些数据:

[{ name: 'Jack', id: 1 }, { name: 'Ellen', id: 2 }, {name: 'Nick', id: 3}]

Apollo 查询会加载此数据。用户可以使用表单向页面添加名称或从页面中删除名称。 Apollo 突变处理添加或删除请求。 RefetchQueries 在突变后被调用,状态被更新并且名称列表被重新呈现。

问题是,如果我添加一个名称,refetchQueries 将返回所有 4 个对象,作为回报,所有 4 个对象都设置为状态。

[{ name: 'Jack', id: 1 }, { name: 'Ellen', id: 2 }, {name: 'Nick', id: 3}, { name: 'Mary', id: 4 }]

我想了解如何获取新旧数据之间的差异。然后从状态数组中添加或删除正确的对象,而不是完全重置状态。使用的 id 将始终是唯一的。

loadData = (names, that) => {

        that.setState({ loading:true });

        let data = {};
        data.names= [];

        const oldNames = that.state.names;

        names.map(name=> (
            data.names.push({ name: name.name, id: name.id })
        ));

       that.setState({names: data.names, loading: false });
}

上面的函数在 componentWillUpdate 中被这样调用:

componentWillUpdate(nextProps, nextState) {
    loadData(nextProps.names, this);
}

我正在尝试更改此功能如下:

updateData = (names, that) => {

        let data = {};
        data.names = [];

        const oldNames = that.state.names ;

        names.map(name => (
            data.names.push({ name: name.name, id: name.id })
        ));

        let difference = _(data.names)
            .differenceBy(oldNames, 'id', 'name')
            .map(_.partial(_.pick, _, 'id', 'name'))
            .value();

        if (data.names.length > oldNames.length)
        that.setState(prevState => ({
            names: [...prevState.names, {name: difference[0].name, id: difference[0].id}]
        }))
}

但是用这种方式找到新旧数据的差异,感觉不是很好。我已经尝试了Compare 2 arrays which returns difference 的解决方案,但我无法让它工作。我希望你能帮助我介绍一种更好的方法来找到差异。在上面的示例中,我使用了 lodash,但这不是必需的。我也想知道有没有更好的方法。例如,直接从 Apollo 突变中获取结果并将此结果添加到 State。

更新节点映射:

render() {

        if (this.state.loading) { return <div>Loading...</div> }

        const links = this.state.links.map( (link,i) => {
            return (
                <Link
                    linkClass={this.props.linkClass}
                    key={link.target+i}
                    data={link}
                />);
        });

        const nodes = this.state.nodes.map( (node) => {
            return (
                <Node
                    nodeClass={this.props.nodeClass}
                    data={node}
                    label={node.label}
                    key={node.id}
                    onClick={this.onClickHandler.bind(this, node)}
                />);
        });

        return (
            <div className="graphContainer">
                <svg className="graph" width={FORCE.width} height={FORCE.height}>
                    <g >
                        {links}
                    </g>
                    <g >
                        {nodes}
                    </g>
                </svg>
            </div>
        );
    }
}

【问题讨论】:

  • 您希望实现什么目标?根据您编写的代码,不会有任何性能提升,因为所有使用 this.state.names 的组件都将重新渲染。如果你想减少 API 调用的负载大小,那么我有一些想法。
  • 使用 d3 显示名称。因此,如果我重新加载整个状态,整个 d3 图就会重新渲染。我只想在 d3 图中添加或删除单个元素。起初我也想找到一种方法来使用 Javascript 来获得差异,但我不知道如何找到它。我想让这部分工作,但任何性能提升都会得到真正的赞赏
  • 添加你使用this.state.names的代码和图形组件。
  • this.state.names 在加载/更新数据时使用,并且仅用于初始化力。我不确定如何显示所有图形代码,它在少数不同的组件中有 300 多行。你在找什么?
  • 问题是,我不确定上面的 lodash 代码中发生了什么,我真的想返回新旧对象数组之间的差异。当我添加名称时,上面的代码有效。但是,如果我删除一个名称,则不会返回已删除的对象。稍后,用户可能会添加或删除多个名称或编辑对象。这段 lodash 代码无法解决所有这些问题。所以我想知道如何获取添加、删除或编辑的对象。我觉得 d3 图表并不是该问题的一部分,如果我错了或没有足够清楚地提出问题,请原谅。

标签: javascript reactjs apollo


【解决方案1】:

当你调用this.setState({ names: newArray })时,newArray必须是一个新构造的数组(因为你不能直接改变状态),所以无论你是根据差异构造这个数组还是只使用结果都没有区别来自 API 调用。

换句话说,setState({ names: [1, 2, 3] })setState({ names: [...someOldState, 3] }) 之间没有区别,因为在这两种情况下您仍在将 names 设置为新建的数组,因此您尝试计算差异不会以任何方式帮助您的意图.

React 优化了仅重新渲染具有更改键的组件,因此您只需确保在映射的组件中提供了 unique and stable 键。

不过,这种优化是在虚拟 DOM 级别完成的。如果您想避免 render 在未更改的 LinkNode 组件中被调用,则需要将它们声明为 PureComponent,或者如果 props 不浅,则需要使用 shouldComponentUpdate 函数。

【讨论】:

  • 非常感谢您的所有努力。我没有意识到 setState 是这样工作的。不过有些奇怪的事情正在发生。当loadData(data) 被添加一个名称调用时,整个图形,包括新名称,都会在容器中间弹出。如果我调用updateData(data),图表会打勾,但新节点会从容器的左上角很好地浮动。两个函数之间的唯一区别是状态设置的方式对吗?为什么会出现不同的行为?
  • 如果this.setState({ names: array }) 中的结果数组是相同的(尝试记录它)它不应该有任何区别,除非你在某处做了一些直接的状态突变。删除loading 状态是否相同?
  • 我删除了loading,它仍然是一样的。那个加载部分显然不再有帮助了。我找到了一种获取已删除对象的方法。然后用一个for循环以一种看似荒谬的方式设置状态并拼接要删除的节点。奇怪的是,我确实得到了想要的结果。 loadDataupdateData 是我调用 setState 的唯一地方。我唯一能想到的另一件事是我在链接和节点组件中使用了 d3 的.datum。它似乎有效,只是我不知道为什么加上这一切似乎有点粗略。
猜你喜欢
  • 2021-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多