Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shortcode attributes in Wordpress plugin with React

I'm trying to figure out how to pass attributes into a react based plugin in Wordpress(using @wordpress/scripts).

my main.php file:

<?php
defined( 'ABSPATH' ) or die( 'Direct script access disallowed.' );

define( 'ERW_WIDGET_PATH', plugin_dir_path( __FILE__ ) . '/widget' );
define( 'ERW_ASSET_MANIFEST', ERW_WIDGET_PATH . '/build/asset-manifest.json' );
define( 'ERW_INCLUDES', plugin_dir_path( __FILE__ ) . '/includes' );

add_shortcode( 'my_app', 'my_app' );
/**
 * Registers a shortcode that simply displays a placeholder for our React App.
 */
function my_app( $atts = array(), $content = null , $tag = 'my_app' ){
    ob_start();
    ?>
        <div id="app">Loading...</div>
        <?php wp_enqueue_script( 'my-app', plugins_url( 'build/index.js', __FILE__ ), array( 'wp-element' ), time(), true ); ?>
    <?php 
    return ob_get_clean();
}

So if I want to load the app with this shortcode [my_app form="login"], how do I pass this attribute to wp_enqueue_script() ? and how can I display a content according to this attribute in react side? I'm trying to display a register form if the attribute is 'register'

my react main file:

import axios from 'axios';
const { Component, render } = wp.element;

class WP_App extends Component { 
constructor(props) {
    super(props);
    this.state = { username: '', password: '' };
    this.handleUsernameChange = this.handleUsernameChange.bind(this);
    this.handlePasswordChange = this.handlePasswordChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
}

render() {
    return (
        <div class="login">
            <form onSubmit={this.handleSubmit} class="justify-content-center">
            <div class="form-group">
                <label htmlFor="username">
                    Username
                </label>
                <input
                    class="form-control"
                    id="username"
                    onChange={this.handleUsernameChange}
                    value={this.state.username}
                    type="email"
                />
             </div>
            <div class="form-group">
                <label htmlFor="password">
                    Password
                </label>
                <input
                    class="form-control"
                    id="password"
                    onChange={this.handlePasswordChange}
                    value={this.state.password}
                    type="password"
                />
            </div>
            <button class="btn btn-primary">
                Submit
            </button>
            </form>
        </div>
    );
}

handleUsernameChange(e) {
    this.setState({ username: e.target.value });
}

handlePasswordChange(e) {
    this.setState({ password: e.target.value });
}

handleSubmit(e) {
    e.preventDefault();
    if (!this.state.username.length) {
        return;
    }
    if (!this.state.password.length) {
        return;
    }
    const creds = { username: this.state.username,
                    password: this.state.password };
    axios.post('https://example.com:8443/login', creds)
        .then(response => {
                console.log("SUCCESSS")
                window.open('https://example.com/login?t=' + response.data.token, "_blank")  
            }
        )
        .catch(error => {
            if (error.response && error.response.data){
                if (error.response.data === "USER_DISABLED"){
                    console.log("User account disabled."
                    )
                }
                if (error.response.data === "ACCOUNT_LOCKED"){
                    console.log("User account is locked probably due to too many failed login attempts."
                    )
                }
                else{
                    console.log("Login failed."
                    )
                }
            }
            else{
                console.log("Login failed."
                )
                
            }
            console.log(error.response)
        });
}
}


render(
    <WP_App />,
    document.getElementById('app')
);
like image 361
Malakiof Avatar asked Jul 23 '20 09:07

Malakiof


People also ask

How do I add parameters to a WordPress shortcode?

To register a shortcode, you have to use the add_shortcode function. The function has two parameters. The first is a unique shortcode tag that represents the name of the shortcode. The second is the name of the handler function (or callback, as it is better known) that is executed after the shortcode is registered.

Can you mix WordPress and React?

Q. Can you use React with WordPress? Yes, React can be used with WordPress, as in the example above, where the content on the front-end built with React is managed by WordPress CMS using the WP Rest API.

How do I echo shortcode in WordPress?

Echo the shortcode. By itself, WordPress do_shortcode just returns a value. If you want to actually display the output of the shortcode, you need to echo the do_shortcode function. If you notice in the WordPress do_shortcode example above, we make sure to always echo the do_shortcode function to display the value.


1 Answers

Wordpress way to load custom data for script is using wp_localize_script function.

You could rewrite your shortcode function by the way

add_shortcode( 'my_app', 'my_app' );
/**
 * Registers a shortcode that simply displays a placeholder for our React App.
 */
function my_app( $atts = array(), $content = null , $tag = 'my_app' ){
    add_action( 'wp_enqueue_scripts', function() use ($atts) {
        wp_enqueue_script( 'my-app', plugins_url( 'build/index.js', __FILE__ ), array( 'wp-element' ), time(), true );
        wp_localize_script(
            'my-app',
            'myAppWpData',
            $atts 
        );
    });

    return '<div id="app">Loading...</div>';
}

Then you could use shortcode settings object via JavaScript by the way:

window.myAppWpData['form'] // if you set form as shortcode param

Then you could set this options as props param for your react WP_App component.

And then you could render your WP_APP content conditionally to its shortcode param:

Main render:

render(
    <WP_App shortcodeSettings={window.myAppWpData} />,
    document.getElementById('app')
);

and how can I display a content according to this attribute in react side?

You could use conditional logic according to shortcode atts values. More details about conditional React logic you could find on official documenction page

https://reactjs.org/docs/conditional-rendering.html

WP_APP render:

you could use props.shortcodeSettings inside WP_APP render() function to build any logic your want to display your component.

render() {
    return (
       // you could use props.shortcodeSettings to build any logic
       // ...your code
    )
}

If you want to have multiple shortcodes on the page.

You could consider to add uniqid( 'my-app' ) to script handle name

function my_app( $atts = array(), $content = null, $tag = 'my_app' ) {
    $id = uniqid( 'my-app' );
    
    add_action( 'wp_enqueue_scripts', function () use ( $atts, $id ) {
        wp_enqueue_script( "my-app", plugins_url( 'build/index.js', __FILE__ ), array( 'wp-element' ), time(), true );
        wp_localize_script(
            "my-app",
            "myAppWpData-$id",
            $atts
        );
    } );

    return sprintf( '<div id="app-%1" data-my-app="%1">Loading...</div>', $id );
}

For this way - you could implement for your index.jsfile logic for multiple apps,

const shortcodesApps = document.querySelectorAll('[data-my-app]');

shortcodesApps.forEach((node) => {
    const nodeID = node.getAttribute('data-my-app');
    const shortcodeSettings = window[`myAppWpData-${nodeID}`];

    render(
        <WP_App shortcodeSettings={shortcodeSettings} />,
        node
    );
})
like image 88
Andrii Kovalenko Avatar answered Nov 06 '22 23:11

Andrii Kovalenko