In backdraftjs can you trigger a component re-render from within the component?

44 Views Asked by At

Here is a backdraftjs component that does not work:

class ClickButton extends Component {
    constructor(kwargs) {
        super(kwargs);
        this.clicked = false;
    }
    
    toggleButton() {
        this.clicked = !this.clicked;
    }
    
    bdElements() {
        return e.div(
            {
                className: this.clicked ? 'clicked' : '',
                bdAdvise: {click: 'toggleButton'}
            },
            'Click Me'
        );
    }
}

...that is, the component doesn't re-render with the "clicked" class after you click it (and of course it doesn't).

I could change it to use watchables, and then trigger the change from outside:

class ClickButton extends Component.withWatchables('isClicked') {    
    bdElements() {
        return e.div(
            {
                bdReflect: {
                    className: ['isClicked', c => c ? 'clicked' : '']
                },
                bdAdvise: {click: this.kwargs.toggleClick}
            },
            'Click Me'
        );
    }
}

But is there some way to keep this all internal to the component? Something like ReactJS component state, where you make a change and it causes the component to re-render and reflect the new state?

1

There are 1 best solutions below

2
On BEST ANSWER

Beginning at the end with the key question:

But is there some way to keep this all internal to the component? Something like ReactJS component state, where you make a change and it causes the component to re-render and reflect the new state?

A ReactJS component state is an aggregate state: it almost-always controls multiple facets of a component (always, with non-trivial components). And mutating one of those facets implies mutating the aggregate state object, which in turn causes a "re-render" of the entire component. You can certainly construct backdraft components to follow this model, but backdraft was not designed to follow such a model (for rational engineering objectives discussed in other forums).

Turning now to the op's particular example, notice that the first solution exposes the property clicked on the public interface of instances of ClickButton. So, clearly, this example does not "keep this all internal to the component".

I think the real question is, "what is the public interface that ClickButton is offering?" I'm guessing op wants:

  1. a button
  2. that has a public read/write boolean property clicked
  3. that toggles its clicked property each time the button is clicked
  4. that sets its DOM class to 'clicked' when the clicked property changes from false to true and conversely

Here is such a component:

class ClickButton extends Component.withWatchables('clicked') {
    bdElements() {
        return e.button(
            {
                bdReflectClass: ['clicked', value=>(value ? 'clicked' : '')],
                bdAdvise_click: e=>(this.clicked = !this.clicked)
            },
            'Click Me'
        );
    }
}

Here's an example of the above code, live in a pen: https://codepen.io/rcgill/pen/eYvrNeB

Perhaps I've failed to understand the thrust of the question and the op is looking for ways to either implement (1) private data, or (2) state mutation implies complete re-render. There are canonical solutions to the former; you can do the latter with backdraft, but if that's the model you prefer, it's probably better to use ReactJS.