Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React: Best way to update self, but prevent children from updating?

I'm working on a Drag-and-drop implementation (from scratch, not using a DND library), and wanted to limit the number of unnecessary updates during drag.

Dragging the "clone" (which is usually a copy of the original element, but can be an arbitrary placeholder) is achieved by updating a state on a container component (the "Clonetainer") and using that to apply a transform. However, it makes no sense to update the entire subtree during a move, since the only change is the coords of the container.

Here's my solution to that:

const ClonetainerRenderShield = React.createClass({
    shouldComponentUpdate: function (newProps) {
        return newProps.shouldUpdate;
    },
    render: function () {
        return this.props.children; // Simple pass-through
    }
});

const Clonetainer = React.createClass({
    componentWillReceiveProps: function (newProps) {
        // OR in any further properties that may indicate a move, versus a child update
        this.isMoveEvent = this.props.offset !== newProps.offset;
    },
    render: function () {
        const style = { transform: `translate(${this.props.offset.left}px,${this.props.offset.top}px)` };
        return <div className="clonetainer-div" style={style}>
            <ClonetainerRenderShield shouldUpdate={ !this.isMoveEvent }>
                { this.props.children }
            </ClonetainerRenderShield>
        </div>;
    }
});

(I won't go into the details on the rest of the DND system, except to say that mouse events from an upstream component feed the offset param to the Clonetainer.)

The solution I came up with for stopping the update involved determining whether the Clonetainer was triggered to update because of a move or some other reason (and setting this.isMoveEvent accordingly), then shimming a component between the Clonetainer and the children consisting of nothing more than a shouldComponentUpdate based upon a passed-in prop (shouldUpdate).

This works. I've tested it in a way that shows that it's updating when it ought to and not updating when it shouldn't, but it feels a bit like overkill to have a separate shim component in there simply to block updates from flowing downhill. Is there a way to indicate that a child component should not be updated from its previous state in render, without requiring the child component to include its own shouldComponentUpdate logic?

like image 914
SuperFLEB Avatar asked Apr 13 '16 15:04

SuperFLEB


1 Answers

You should be able to change componentWillReceiveProps to shouldComponentUpdate in the Clonetainer component and cut out the middle man. shouldComponentUpdate takes two parameters, (object nextProps, object nextState) which you can use to compare agains this.state and this.props. Returning true will cause a re-render.

const Clonetainer = React.createClass({
    shouldComponentUpdate: function (nextProps, nextState) {
        // OR in any further properties that may indicate a move, versus a child update
        this.props.offset !== nextProps.offset;
    },
    render: function () {
        const style = { transform: `translate(${this.props.offset.left}px,${this.props.offset.top}px)` };
        return <div className="clonetainer-div" style={style}>
            { this.props.children }
        </div>;
    }
});
like image 121
Erd Avatar answered Nov 15 '22 01:11

Erd