Synchronizing Perspective Component Props with State

I am currently doing the following to keep my component props updated with the state of my component, however I feel like I am making this more complicated than it needs to be.. Is there a simpler way to update the prop checked in this example and keep it in sync with state.isChecked without having to write to it every time like this? Needing to call the props.write as well as the this.setState right beside each other is the part I am mostly curious about.

export interface CheckboxProps {
    text?: string;
    checked?: boolean;
}

export class Checkbox extends Component<ComponentProps<CheckboxProps>, any> {
	constructor(props: ComponentProps<CheckboxProps>) {
		super(props);
		this.state = {
			isChecked: props.props.checked || false
		};
	}

    handleCheckboxChange = (event: React.ChangeEvent<HTMLInputElement>) => {
		this.props.store.props.write('checked', event.target.checked);
		this.setState({
			isChecked: event.target.checked
		});
    }

    render() {
		const { props: { text, checked }, emit } = this.props;

        return (
            <div {...emit()}>
                <label>
                    <input
                        type="checkbox"
                        checked={checked}
                        onChange={this.handleCheckboxChange}
                    />
                    {text}
                </label>
            </div>
        );
    }
}

Also, a separate question. Is it a best practice to use the props or the state when rendering the component? I could imagine that in some scenarios I want state elements that aren't props, in which I would need to use the state. However in this scenario I am curious if I have a state element that I have "synchronized" to a prop?

For additional context, this is how I found that.

Just trying to understand if thats the common strategy. Whenever you update the state to also call the props.store.props.write function

My opinion; duplicate/redundant state is evil, and Perspective props should be preferred over internal state 99.99% of the time.

A Perspective component has different goals than a typical React component. The end user of a Perspective component is expecting to drag components into a view and configure them with bindings/change-scripts, both of which require the Perspective props to be the single source of truth.

If you have a complex use case where a Perspective component changes/modifies its state, cool, expose that as Perspective props so the end user can see and work with it.

5 Likes

So to confirm, would this be the ideal structure for any props that a user may need to interact with?

export interface CheckboxProps {
    text?: string;
    checked?: boolean;
}

export class Checkbox extends Component<ComponentProps<CheckboxProps>, any> {

    handleCheckboxChange = (event: React.ChangeEvent<HTMLInputElement>) => {
        this.props.store.props.write('checked', event.target.checked);
    }

    render() {
        const { props: { text, checked }, emit } = this.props;
        return (
            <div {...emit()}>
                <label>
                    <input
                        type="checkbox"
                        checked={checked}
                        onChange={this.handleCheckboxChange}
                    />
                    {text}
                </label>
            </div>
        );
    }
}

Yup, that’s how I would do it.

And to test, I’d put two of the components on a view and bidirectionally bind their checked properties together. Clicking one checkbox should write to its props, trigger the bidirectional binding, which updates the props of the other checkbox triggering a re-render.

3 Likes