Visx: Controlled Brush

Created on 6 Jan 2020  路  3Comments  路  Source: airbnb/visx

I have a use case for a brush that can be initialized with some state, more like a controlled component rather than an uncontrolled component (as it currently maintains it's own state).

For example on initial render there would already be a brushed area that the user can then drag to resize, rather than always starting from an un-brushed state.

I think this could be achieved by optionally passing in brush state rather than the brush managing it's own state. Is there appetite to support this use-case? I'm happy to submit a PR if so.

馃憢help wanted 馃挰discussion

Most helpful comment

Hey @hellosmithy 馃憢 thanks for the suggestion, I think there's definitely a use case for this!

If not too complex to implement, it seems like we could have a dual controlled/uncontrolled component where you can optionally pass in the brush state as you say, but if you don't it will manage it on its own as it does now.

Happy to review a PR or discuss implementation details more! 馃檶

All 3 comments

Hey @hellosmithy 馃憢 thanks for the suggestion, I think there's definitely a use case for this!

If not too complex to implement, it seems like we could have a dual controlled/uncontrolled component where you can optionally pass in the brush state as you say, but if you don't it will manage it on its own as it does now.

Happy to review a PR or discuss implementation details more! 馃檶

we could have a dual controlled/uncontrolled component where you can optionally pass in the brush state

Yea that's my thoughts too. OK - I'll put together a PR then for you to review.

I was looking for a way to control the Brush component by giving it a brushPosition props. Here's my solution :

import React, { useEffect, useState } from "react";
import { Brush } from "@visx/brush";

const positionToKey = ({ start, end }) => `${start.x},${start.y},${end.x},${end.y}`;

const ControlledBrush = ({ brushPosition, domainToPosition, ...brushProps }) => {
  const [innerPosition, setInnerPosition] = useState(brushPosition);
  const [key, setKey] = useState(positionToKey(brushPosition));

  const { onChange } = brushProps;

  useEffect(() => {
    if (positionToKey(innerPosition) !== positionToKey(brushPosition)) {
      setKey(positionToKey(brushPosition));
    }
  }, [innerPosition, brushPosition]);

  return (
    <Brush
      key={key}
      {...brushProps}
      initialBrushPosition={brushPosition}
      onChange={(domain) => {
        if (domain !== null) {
          setInnerPosition(domainToPosition(domain));
        }
        return onChange && onChange(domain);
      }}
    />
  );
};

export default ControlledBrush;

The key props change only when an external change to brushPosition is detected. This make it compatible with brush, move & resize interactions.

You have to specify the domainToPosition props witch is a function used to convert domain ({x0, x1, y0, y1}) into position ({start: {x, y}, end:{x, y}}). There's probably a better way to handle this part.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

danielprogramic picture danielprogramic  路  3Comments

OKNoah picture OKNoah  路  5Comments

sabarnix picture sabarnix  路  4Comments

ghost picture ghost  路  5Comments

goaaron picture goaaron  路  3Comments