Editorial data and props

Contents

Editorial data and props

Lesson overview

Here we’ll spice up our react component a little bit, by using props to insert editorial data from XP into the react component.

We’ll modify the same files you created previously:

Files involved:
site/pages/hello-react/
  hello-react.xml
  hello-react.jsx
  hello-react.es6


Code

Let’s change the files some more so we can use Content Studio to input some data into the react rendering.


Page definition

We start with some standard XP procedure: defining the data in the page definition XML file. This file is the same as in example 1, but we’re adding a few input fields under <form>: greeting, greetee, counted and startCount are the basis for the initial props of the react component:

hello-react.xml:
<page>
    <display-name>Hello React</display-name>
    <description>Editorial example</description>
    <form>
        <input type="TextLine" name="greeting">
            <label>What's the greeting?</label>
            <default>Hello</default>
            <occurrences minimum="1" maximum="1"/>
        </input>

        <input type="TextLine" name="greetee">
            <label>Who shall we greet?</label>
            <default>world</default>
            <occurrences minimum="1" maximum="1"/>
        </input>

        <input type="TextLine" name="things">
            <label>What are the things on the wall?</label>
            <default>bottles of beer</default>
            <occurrences minimum="1" maximum="1"/>
        </input>

        <input type="Long" name="startCount">
            <label>How many of them are there?</label>
            <default>99</default>
            <occurrences minimum="1" maximum="1"/>
        </input>
    </form>
</page>


React component

Next, we’ll modify the react component so that it displays data from a props object, instead of hardcoding everything.

hello-react.jsx
import React from 'react';


function makeThingDropper(droppableProp, initialCountProp) {
    let currentCount = initialCountProp;
    return () => {
        currentCount--;
        console.log(currentCount.toString(), droppableProp, 'on the wall.');
        document.getElementById('counter').innerText = currentCount;
    };
}

export default (props) => {
    const dropThing = makeThingDropper(props.droppableThing, props.initialCount);
    return (
        <div onClick={dropThing}>
            <h1>
                {props.message} {props.messageTarget}!
            </h1>
            <p>
                Click me: <span id="counter">{props.initialCount}</span> {props.droppableThing} on the wall.
            </p>
        </div>
    );
}

props are of course the standard react way to do this. It all is. As long as a props ⇒ reactComponent function is default-exported, react4xp accepts standard valid ES6/JSX.

Although, if you think that the makeObjectDropper closure thing is a strange way to do something react itself could do better…​ sure. Just trying to stay focused on one thing at a time.


Page controller

And lastly, we’ll hook them together: modify the controller to fetch the data we defined in XML, then use a props object to inject the data into the react component:

hello-react.es6:
const portal = require('/lib/xp/portal');
const React4xp = require('/lib/enonic/react4xp');


exports.get = function(request) {
    const entry = portal.getComponent();

    const content = portal.getContent();                    (1)
    const pageConfig = (content.page || {}).config || {};

    const props = {                                         (2)
        message: pageConfig.greeting,                       (3)
        messageTarget: pageConfig.greetee,
        droppableThing: pageConfig.things,
        initialCount: pageConfig.startCount
    };

    return React4xp.render(
        entry,
        props,                                              (4)
        request,
        {
            id: "react4xpApp",
            body: `
                <html>
                    <head></head>
                    <body class="xp-page">
                        <div id="react4xpApp"></div>
                    </body>
                </html>
            `
        }                                                   (5)
    )
};
1 Fetching the content data defined in the XML (and in the next line, we’re unpacking it into pageConfig while choosing that missing data should just display emptiness, not throw an error).
2 The props object is just any standard JS object. So the data can of course come from anywhere you want and take any shape - with one exception: props needs to be serializable, so functions can’t be passed here!
3 Connecting the field names in pageConfig from hello-react.xml to the prop names that hello-react.jsx needs.
4 See how this makes the first two arguments of React4xp.render analogous to XP’s thymeleaf.render template engine? The first argument, entry, is just a reference to what should be rendered (react component ~ template), and the second one is a collection of data injected into it (props ~ model).
5 To keep things simpler and clearer, just remove the clientRender flag from the previous example.

There it is, now let’s take a look!


Setup and rendering

Compile the project, enter Content Studio (see the first two steps in the previous setup), and edit the content you created (double-click it to open a new tab).

You should still see it in the preview panel on the right (although, since you probably created the content without any data created along with it, it might not display much of the text. We’ll fix that):

hello cs


Now, when you click the preview panel, the page-config editing panel should open on the right, with the data fields containing the default text we defined. Once you click Apply/save, the preview panel to the left should update.

hello editorial


So now, it looks the same as before, but with editorial data instead of hardcoded text. Boring, and too similar to the previous example; just repeating "Hello World" might cause a little confusion. Try adding your own data in the fields, for example changing "world" into "Bruce" etc, to keep it clear.

Apply/save your new props, and the output should change again. But since we’re still in Content Studio, it’s just a static serverside-rendered update. To see the final rendering with your new data, all active, click Preview on the top to open the page in a fresh tab:

gday bruce


Output

So did anything change in the rendered response, compared to the first serverside-rendered example? Not all that much, actually. Depending on what data you inserted and the resulting props, your page source should look something like this:

<html>
<head></head>
<body class="xp-page">

    (1)
    <div id="react4xpApp">
        <div data-reactroot="">
            <h1>G'day<!-- --> <!-- -->Bruce<!-- --> !</h1>
            <p>Click me: <span id="counter">42</span> <!-- -->tubes<!-- --> on the wall.</p>
        </div>
    </div>

    (2)
    <script src="(...your.app.service) /react4xp/externals.489d97cdf.js"></script>
    <script src="(...your.app.service) /react4xp-client/"></script>
    <script src="(...your.app.service) /react4xp/site/pages/hello-react/hello-react.js"></script>

    <script defer>
        React4xp.CLIENT.hydrate(
            React4xp['site/pages/hello-react/hello-react'],
            "react4xpApp",
            {                           (3)
                "message": "G'day",
                "messageTarget": "Bruce",
                "droppableThing": "tubes",
                "initialCount": 42,
                "react4xpId": "react4xpApp"
            },
            1, 0
        );
    </script>
</body>
</html>
1 Since we removed the clientRender flag again, the target container react4xpApp comes filled from the server. But now with a rendering with your texts from props already inserted.
2 There’s still no change in the asset URLs, but since we changed hello-react.jsx, the content of hello-react.js has of course been recompiled.
3 The props are used in the clientside .hydrate call. For convenience, the id is automatically added as the react4xpId prop. Handy for use cases where a react component needs to uniquely identify itself.


Further reading

Now might be a good time to take a closer look at the API overview of the .render call from the ES6 controller in the example above.


Either way, you should be ready for the last of the three basic lesson chapters.



Contents