3

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.

  • i guess not. the state is local. maybe InnerBlocks are the right for your requirements: github.com/WordPress/gutenberg/tree/master/packages/editor/src/… so you could nest two blocks.. – André Kelling Jan 8 at 10:50
  • @AndréKelling, I've edited the question to say I'm happy to achieve this in any other way that's possible, so if the state is not available, perhaps props can be used. – Sean Jan 8 at 10:52
  • if you need that value inside the react components or subcomponents state and props are the way to go. but if you need that data in an independent custom JS script your solution looks very good! – André Kelling Jan 8 at 11:53
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.

  • Thanks! I ended up implementing a similar solution to what you posted here in my code before you posted this. I used a higher-order component to inject some extra data that I needed in my solution, but hooking into the withSelect and withDispatch functions is what I used too. Here is my working code for reference. – Sean Jan 9 at 19:13
1

I found a solution, but I'm not sure it's really the intended way to do this. I use the core editor datastore to set the value, then retrieve it from there in my function.

In the onChange callback in MyPlugin, in addition to setting the state I also save the value:

onChange: ( value ) => {
    this.setState( {
        value
    })

    // set data in store
    wp.data.select('core/editor').my_custom_key = value;
}

then in my function I just grab it out:

const myFunction = () => {
    var myData = wp.data.select('core/editor').my_custom_key;

    // do stuff with myData...
}
  • 1
    You are in the right path using the store, but the proper way is to create your own store, because you need to create the reducer, selector and action, at least. Take a look at data. – Alvaro Jan 8 at 11:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.