比较2个对象数组,一个用旧数据,一个用新数据,然后根据新数据

时间:2018-04-27 05:37:52

标签: javascript reactjs apollo

我的用户在此页面上有一些数据:

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

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

问题是,如果我添加一个名称,refetchQueries将返回所有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}]
        }))
}

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

UPDATE节点映射:

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>
        );
    }
}

1 个答案:

答案 0 :(得分:1)

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

换句话说,setState({ names: [1, 2, 3] })setState({ names: [...someOldState, 3] })之间没有区别,因为在这两种情况下您仍然将names设置为新构建的数组,因此您尝试计算这种差异无论如何都无助于你的意图。

React优化仅使用更改的密钥重新渲染组件,因此您只需确保映射组件中提供了unique and stable密钥。

此优化是在虚拟DOM级别完成的。如果您想避免在未更改的renderLink组件中调用Node,则需要将它们声明为PureComponent,或者使用shouldComponentUpdate函数进行调用道具不浅。