Javascript 比较两个对象数组,一个使用旧数据,一个使用新数据,然后根据新数据更新状态

Javascript 比较两个对象数组,一个使用旧数据,一个使用新数据,然后根据新数据更新状态,javascript,reactjs,apollo,Javascript,Reactjs,Apollo,我有一个用户,该页面上有以下表单中的一些数据: [{ name: 'Jack', id: 1 }, { name: 'Ellen', id: 2 }, {name: 'Nick', id: 3}] Apollo查询加载此数据。用户可以使用表单在页面中添加或删除名称。Apollo变异处理添加或删除请求。在变异后调用RefetchQueries,更新状态并重新检索名称列表 问题是,如果我添加一个名称,refetchquerys将返回所有4个对象,作为返回,所有4个对象都将设置为state [{ n

我有一个用户,该页面上有以下表单中的一些数据:

[{ name: 'Jack', id: 1 }, { name: 'Ellen', id: 2 }, {name: 'Nick', id: 3}]
Apollo查询加载此数据。用户可以使用表单在页面中添加或删除名称。Apollo变异处理添加或删除请求。在变异后调用RefetchQueries,更新状态并重新检索名称列表

问题是,如果我添加一个名称,refetchquerys将返回所有4个对象,作为返回,所有4个对象都将设置为state

[{ 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}]
        }))
}
但是以这种方式发现新旧数据之间的差异感觉不是很好。我尝试了来自的解决方案,但无法使其发挥作用。我希望你能帮我介绍一种更好的方法来找出差异。在上面的例子中,我使用了lodash,但这不是一个要求。我还想知道是否有更好的办法。例如,直接从阿波罗突变中获得结果,并将该结果添加到状态

更新节点的映射:

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>
        );
    }
}
render(){
如果(this.state.load){返回加载…}
const links=this.state.links.map((link,i)=>{
返回(
);
});
const nodes=this.state.nodes.map((节点)=>{
返回(
);
});
返回(
{links}
{nodes}
);
}
}

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

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

React只优化使用更改的关键帧重新渲染组件,因此您只需确保在映射的组件中提供关键帧


不过,这种优化是在虚拟DOM级别完成的。如果要避免在未更改的
链接
节点
组件中调用
render
,则需要将它们声明为
PureComponent
,如果道具不浅,则使用
shouldComponentUpdate
函数

你希望达到什么目标?根据您编写的代码,不会有任何性能提升,因为使用
this.state.names
的所有组件都将重新呈现。如果您想减少API调用的有效负载大小,那么我有一些想法。使用d3显示名称。因此,如果我重新加载整个状态,整个d3图形将重新加载。我只想在d3图中添加或删除单个元素。起初,我还想找到一种使用Javascript获得差异的方法,但我不确定如何找到它。我想让这部分正常工作,但任何性能提升都将非常有助于添加使用
this.state.names
和图形组件的代码。
this.state.names
在加载/更新数据时使用,并且仅用于初始化力。我不知道如何显示所有的图形代码,在几个不同的组件中有300多行。你在寻找什么?问题是,我不确定上面的lodash代码中到底发生了什么,我真的想返回新旧对象数组之间的差异。当我添加一个名称时,上面的代码起作用。但如果我删除了一个名称,则不会返回已删除的对象。稍后,用户可能会添加或删除多个名称或编辑对象。这个lodash代码帮不了你。。因此,我想知道如何才能获得添加、删除或编辑的对象。我觉得d3图不是问题的一部分,如果我错了或者问得不够清楚,请原谅。非常感谢你的努力。我不知道setState是这样工作的。但奇怪的事情正在发生。当<代码>加载数据(数据)< /C> >用一个添加的名称调用时,整个图形(包括新名称)弹出到容器的中间。如果我调用
updateData(data)
,图形会打勾,但是新节点很好地从容器的左上角浮动进来。两个函数之间的唯一区别是状态设置的正确方式?为什么会发生不同的行为?如果
this.setState({names:array})
中的结果数组是相同的(请尝试记录),则不会有什么区别,除非您在某个地方进行了一些直接状态突变。删除
加载
状态是否会使其保持不变?我删除了
加载
,但它仍然保持不变。加载部分显然不再有帮助了。我找到了一种获取已删除对象的方法。然后使用for循环以看似荒谬的方式设置状态,并拼接要删除的节点。令人窒息的是,我确实得到了想要的结果
loadData
updateData
是我调用setState的唯一位置。我唯一能想到的另一件事是在链接和节点组件中使用d3的
.datum
。这似乎是可行的,只是我不知道为什么,加上这一切似乎有点粗略。