0

I am building a Ecommerce Wordpress/Woocommerce Theme using React.js. I am using the npm package:

create-react-wptheme

This package renders the react app directly inside of the index.php of the wordpress theme.

I am unsure how to access the wordpress api endpoints inside of react.js components. I have created the templates for the site using jsx, and need to access the api Endpoints to render dynamic content. I can access the points using http requests to the external api, but I need to be able to access the full api inside of the react-wp-theme.

From what I understand I need to use the backbone.js api client to access the wp rest-api using react.

Or do i possibly need a npm package to hook this up.

How can I make the api available inside of my app.js and components?

I want to make a basic GET request to fetch the posts object, but I do not know how to import the wp Rest-API into react.

Here is a post page where I am trying to fetch a post. It brings up an error. Here is the page:

    import React from "react";


    class Post extends React.Component {
      render() {
        let wpPosts = new window.wp.api.collections.Posts();
wpPosts.fetch({ data: { per_page: 2 } }).done(posts => {
          posts.forEach(post => {
            console.log(post.title.rendered);
        });
        return (
          <div className="container indigo lighten-4">
            <div className="container flow-text py-5 mt-5">
              <h1>Title</h1>
              <p>
                content
              </p>
            </div>
          </div>
        );
      }
    }

    export default Post;

This Page Does not display here is the error from the console.

    Post.js:6 Uncaught TypeError: Cannot read property 'api' of undefined
    at Post.render (Post.js:6)
    at finishClassComponent (react-dom.development.js:15141)
    at updateClassComponent (react-dom.development.js:15096)
    at beginWork (react-dom.development.js:15980)
    at performUnitOfWork (react-dom.development.js:19102)
    at workLoop (react-dom.development.js:19143)
    at HTMLUnknownElement.callCallback (react-dom.development.js:147)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:196)
    at invokeGuardedCallback (react-dom.development.js:250)
    at replayUnitOfWork (react-dom.development.js:18350)
render @ Post.js:6
finishClassComponent @ react-dom.development.js:15141
updateClassComponent @ react-dom.development.js:15096
beginWork @ react-dom.development.js:15980
performUnitOfWork @ react-dom.development.js:19102
workLoop @ react-dom.development.js:19143
callCallback @ react-dom.development.js:147
invokeGuardedCallbackDev @ react-dom.development.js:196
invokeGuardedCallback @ react-dom.development.js:250
replayUnitOfWork @ react-dom.development.js:18350
renderRoot @ react-dom.development.js:19261
performWorkOnRoot @ react-dom.development.js:20165
performWork @ react-dom.development.js:20075
performSyncWork @ react-dom.development.js:20049
requestWork @ react-dom.development.js:19904
scheduleWork @ react-dom.development.js:19711
scheduleRootUpdate @ react-dom.development.js:20415
updateContainerAtExpirationTime @ react-dom.development.js:20441
updateContainer @ react-dom.development.js:20509
push../node_modules/react-dom/cjs/react-dom.development.js.ReactRoot.render @ react-dom.development.js:20820
(anonymous) @ react-dom.development.js:20974
unbatchedUpdates @ react-dom.development.js:20292
legacyRenderSubtreeIntoContainer @ react-dom.development.js:20970
render @ react-dom.development.js:21037
./src/index.js @ index.js:6
__webpack_require__ @ bootstrap:78
0 @ serviceWorker.js:135
__webpack_require__ @ bootstrap:78
checkDeferredModules @ bootstrap:45
webpackJsonpCallback @ bootstrap:32
(anonymous) @ main.chunk.js:1
react-dom.development.js:16764 The above error occurred in the <Post> component:
    in Post (created by Route)
    in Route (at Routes.js:29)
    in Switch (at Routes.js:20)
    in Routes (at App.js:18)
    in div (at App.js:16)
    in Router (created by BrowserRouter)
    in BrowserRouter (at App.js:15)
    in App (at src/index.js:6)

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://fb.me/react-error-boundaries to learn more about error boundaries.
logCapturedError @ react-dom.development.js:16764
logError @ react-dom.development.js:16800
update.callback @ react-dom.development.js:17814
callCallback @ react-dom.development.js:11743
commitUpdateEffects @ react-dom.development.js:11783
commitUpdateQueue @ react-dom.development.js:11773
commitLifeCycles @ react-dom.development.js:17055
commitAllLifeCycles @ react-dom.development.js:18512
callCallback @ react-dom.development.js:147
invokeGuardedCallbackDev @ react-dom.development.js:196
invokeGuardedCallback @ react-dom.development.js:250
commitRoot @ react-dom.development.js:18717
completeRoot @ react-dom.development.js:20247
performWorkOnRoot @ react-dom.development.js:20170
performWork @ react-dom.development.js:20075
performSyncWork @ react-dom.development.js:20049
requestWork @ react-dom.development.js:19904
scheduleWork @ react-dom.development.js:19711
scheduleRootUpdate @ react-dom.development.js:20415
updateContainerAtExpirationTime @ react-dom.development.js:20441
updateContainer @ react-dom.development.js:20509
push../node_modules/react-dom/cjs/react-dom.development.js.ReactRoot.render @ react-dom.development.js:20820
(anonymous) @ react-dom.development.js:20974
unbatchedUpdates @ react-dom.development.js:20292
legacyRenderSubtreeIntoContainer @ react-dom.development.js:20970
render @ react-dom.development.js:21037
./src/index.js @ index.js:6
__webpack_require__ @ bootstrap:78
0 @ serviceWorker.js:135
__webpack_require__ @ bootstrap:78
checkDeferredModules @ bootstrap:45
webpackJsonpCallback @ bootstrap:32
(anonymous) @ main.chunk.js:1
react-dom.development.js:20135 Uncaught TypeError: Cannot read property 'api' of undefined
    at Post.render (Post.js:6)
    at finishClassComponent (react-dom.development.js:15141)
    at updateClassComponent (react-dom.development.js:15096)
    at beginWork (react-dom.development.js:15980)
    at performUnitOfWork (react-dom.development.js:19102)
    at workLoop (react-dom.development.js:19143)
    at renderRoot (react-dom.development.js:19228)
    at performWorkOnRoot (react-dom.development.js:20165)
    at performWork (react-dom.development.js:20075)
    at performSyncWork (react-dom.development.js:20049)

Here is my functions.php that I created to access the api.

<?php

function my_theme_scripts() {
  // Enqueue the Backbone JavaScript Client.
  wp_enqueue_script( 'wp-api' );
}
add_action( 'wp_enqueue_scripts', 'my_theme_scripts' );

?>

Here is My index.php

<?php
  $TEMPLATE_PATH = parse_url(get_template_directory_uri(), PHP_URL_PATH);
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
        <!-- Material Icons -->
        <link
      href="https://fonts.googleapis.com/icon?family=Material+Icons"
      rel="stylesheet"
    />
    <!-- Font Awesome -->
    <link
      rel="stylesheet"
      href="https://use.fontawesome.com/releases/v5.6.3/css/all.css"
      integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/"
      crossorigin="anonymous"
    />
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="theme-color" content="#000000">
    <link rel="manifest" href="<?php echo $TEMPLATE_PATH; ?>/manifest.json">
    <?php wp_head(); ?> 

    <title>React WordPress Theme</title>
</head>
    <body>
    <noscript>
        You need to enable JavaScript to run this app.
    </noscript>
    <div id="root"></div>

    </body>
    <?php wp_footer(); ?>
</html>

Here is my App.js

import React, { Component } from "react";
import "./scss/App.scss";
import "./scss/pbml.scss";
import Headers from "./routers/Headers";
import Footer from "./components/footer/Footer";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";

import "materialize-css/dist/css/materialize.min.css";

import Routes from "./routers/Routes";

class App extends Component {
  render() {
    return (
      <Router>
        <div className="cosmos">
          <Headers />
          <Routes />
          <Footer />
        </div>
      </Router>
    );
  }
}

export default App;

And Here is my index.js that just points the root to my app.js

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import * as serviceWorker from "./serviceWorker";

ReactDOM.render(<App />, document.getElementById("root"));
  • This might help. – Sally CJ Feb 6 at 1:42
  • I have read the documentation, but I am having difficulty importing the rest-api into my react components. I updated the question to include more info. – Tony Wheeler Feb 6 at 3:38
  • You enqueue scripts on the front-end using the wp_enqueue_scripts hook - e.g. In functions.php, do add_action( 'wp_enqueue_scripts', function(){ wp_enqueue_script( 'wp-api' ); } ). – Sally CJ Feb 6 at 3:49
  • But, you don't have wp_head(); in your template (index.php), so you could put it there - before </head>, or try adding wp_print_scripts( 'wp-api' ); before </body>.. – Sally CJ Feb 6 at 4:04
  • Thanks for your help. I have expanded the project to include a functions.php, and added the wp_head and wp_footer to my index.php. I still have the same error, because Im not sure how to access the wp-api in react. What do I call in the js file to import wp? – Tony Wheeler Feb 6 at 6:40
0

I can access the points using http requests to the external api, but I need to be able to access the full api inside of the react-wp-theme.

You could use either the wp-api script / Backbone JavaScript client library or one of the other client libraries listed here such as the node-wpapi library.

Using the wp-api script

  1. Enqueue the script:

    wp_enqueue_script( 'wp-api' );
    
  2. In index.php, make sure <?php wp_head(); ?> is added in the document head, and <?php wp_footer(); ?> in the body.

And then in your React components, access the client via window.wp.api instead of just wp.api:

const postsCollection = new window.wp.api.collections.Posts(); // like this
//const postsCollection = new wp.api.collections.Posts();      // not this

Here's an example React Component which uses the Posts collection to retrieve one post: (the markup is based on your Post component)

import React from "react";

class Post extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      error: null,
      isLoaded: false,
      item: {}
    };
  }

  componentDidMount() {
    // Instantiates the collection.
    const postsCollection = new window.wp.api.collections.Posts();

    // Retrieves one post.
    postsCollection.fetch({ data: { per_page: 1 } })
      .done(posts => {
        this.setState({
          isLoaded: true,
          item: posts[0]
        });
      })
      .error((xhr, status, error) => {
        this.setState({
          isLoaded: true,
          error
        });
      });
  }

  render() {
    const { error, isLoaded, item } = this.state;
    if (error || item.errors) {
      return <div>Error: {error || 'API error...'}</div>;
    } else if (! isLoaded) {
      return <div>Loading...</div>;
    } else {
      return (
        <div className="container indigo lighten-4">
          <div className="container flow-text py-5 mt-5">
            <h3>{item.title.rendered}</h3>
            <p>{item.excerpt.rendered}</p>
          </div>
        </div>
      );
    }
  }
}

export default Post;

Tried and tested working, and the code is based on the official example.

Using the node-wpapi library

You can refer to the documentation on installing the library and using it.

But here's the above Post component, adapted to using the node-wpapi library: (the ... means nothing changed)

import React from "react";

const WPAPI = require('wpapi');
const wp = new WPAPI({
  endpoint: 'http://example.com/wp-json'
});

class Post extends React.Component {
  constructor(props) {
    ...
  }

  componentDidMount() {
    // Retrieves one post.
    wp.posts().perPage(1)
      .then(posts => {
        this.setState({
          isLoaded: true,
          item: posts[0]
        });
      })
      .catch(err => {
        this.setState({
          isLoaded: true,
          error: err.message
        });
      });
  }

  render() {
    ...
  }
}

export default Post;

The Full Code

You can check my files here.

  • Thank you Sally. It seems that enqueing the script in the index let me access the backbone api. It works now. :) – Tony Wheeler Feb 16 at 23:00
  • You're welcome, I'm glad you got it working. :) I did suspect the wp-api script was not enqueued (since you had already attempted window.wp.api), but in my case, enqueuing the script via functions.php also did work. :) – Sally CJ Feb 16 at 23:34

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.