Getting a custom Gutenberg component’s state from outside that component

I’ve registered a sidebar component in the block editor. I’d like to access the state of the component (MyPlugin) from outside of that component, in some arbitrary JavaScript in the same file. Is it possible to access the state via e.g. the wp object?

Or, is it possible, and would it be better to do this using props?

class MyPlugin extends Component {
    constructor() {
        super( ...arguments );

        // initial state
        this.state = {
            key: 'my_key',
            value: ''
        }
    }

    render() {
        return el(
            PluginPostStatusInfo,
            {
                className: 'my-panel-class'
            },
            el(
                TextControl,
                {
                    name: 'my_text_control_name',
                    label: __( 'My label', 'my-text-domain' ),
                    value: this.state.value,
                    onChange: ( value ) => {
                        this.setState( {
                            value
                        })
                    }
                }
            )
        );
    }
}

registerPlugin( 'my-plugin', {
    render: MyPlugin
} );

const myFunction = () => {
    // this function is hooked into an action elsewhere,
    // and needs to access the state of MyPlugin
}

What I’d ultimately like to do is access the textbox’s value from a separate function. I don’t mind how this is achieved, e.g. with state or props or some other means. I guess another approach would be to have the component write its value into a global variable when it changes, but that seems a bit clunky.

2 Answers
2

To do so you need to use a redux store. To register your own you can follow the steps in the documentation. Here is the minimum that should achieve what you are looking for.

First, lets give the “shape” of the store in the initial state object:

const initial_state = {
    my_control: {
        value: ""
    },
};

Lets create a reducer that manipulates the state:

const reducer = (state = initial_state, action) => {
    switch (action.type) {
        case "UPDATE_MY_CONTROL_VALUE": {
            return {
                ...state,
                my_control: {
                    ...state.my_control,
                    value: action.value
                }
            };
        }
    }

    return state;
};

As you can see, we set the initial_state object we just created as the default value of the state.

Now lets add an action which updates the store, sending data to the reducer:

const actions = {
    updateMyControlValue(value) {
        return {
            type: "UPDATE_MY_CONTROL_VALUE",
            value
        };
    },
}

Lets add a selector that gets the data from the store:

const selectors = {
    getMyControlValue(state) {
        return state.my_control.value;
    },
};

And now we will register the store with the previous constants:

const { registerStore } = wp.data;

registerStore("my_plugin/my_store", {
    reducer,
    actions,
    selectors
});

Now the store is registered. We will connect the component to the store to get the latest value of my_control and to update it:

const { Component } = wp.element;
const { TextControl } = wp.components;
const { compose } = wp.compose;
const { withDispatch, withSelect } = wp.data;

class Text extends Component {
    render() {
        const { value, updateMyControlValue } = this.props;

        return (
            <TextControl
                value={value}
                onChange={updateMyControlValue}
            />
        );
    }
}

export default compose([
    withDispatch((dispatch, props) => {
        // This function here is the action we created before.
        const { updateMyControlValue } = dispatch("my_plugin/my_store");

        return {
            updateMyControlValue
        };
    }),
    withSelect((select, props) => {
        // This function here is the selector we created before.
        const { getMyControlValue } = select("my_plugin/my_store");

        return {
            value: getMyControlValue()
        };
    }),
])(Text);

This is a simple example, but you can take a look at the link on the documentation to see how you can use other functionality to enhance the store (for example, with controls and resolvers).

Also you can make use the core stores and their selectors and actions inside your component’s withSelect and withDispatch.

Leave a Comment