35
loading...
This website collects cookies to deliver better user experience
create-react-app
, which is what's recommended in the official docs and is an opinionated, batteries included tool to quickly scaffold React applications. You can add it globally with the usual yarn global add create-react-app
or npm install -G create-react-app
, but I don't want it to stay on my computer since I will only use the command once per project, so I'll use npx
to execute the binary from a temporary location and then remove it.npx create-react-app wordpress-react
cd wordpress-react
and use yarn start
to get the live development server with Webpack rolling. I do not mean for this to be an introduction to React. The documentation and the countless tutorials available online will be more than enough to get you started. I'll show how to integrate the WP REST API with react-router
and I'll take this chance to make use of the new Context API of React 16.3+.yarn add react-router react-router-dom
- src
| - components
| - containers
| > Store.js
| > App.js
| > index.js
App.js
:import React from 'react';
const App = () => <div>Hello, WordPress!</div>;
export default App;
docker-compose.yml
, which will tell Docker to add our front-end to the network. The npm run start
process will run inside a Docker container, but we'll mount our source directory as a volume, which will mean that any change we make during development will trigger the watcher inside the container and will reload the app just as if it were running on the host machine.create-react-app
created for us. Let's add this new service to the docker-compose.yml
:app:
depends_on:
- wordpress
build: .
ports:
- '3000:3000'
volumes:
- '.:/app'
build: .
Dockerfile
in the same directory, which we do not yet have. Let's fix that:FROM node:alpine
RUN mkdir -p /app
COPY ./package.json /app/package.json
WORKDIR /app
RUN npm install
ENTRYPOINT [ "npm", "start" ]
package.json
for NPM to be able to install our dependencies, before the mounting happens. This will mean that whenever we restart the container, it will not have to pull dependencies every time. One more thing we need to add in order for the React App to reach the WordPress instance is the proxy
config field in the package.json
file, like so:{
...
"proxy": "http://wordpress"
}
http://localhost
on our computers, however that is not how it can be accessed inside the network, which is the reason why we added it to a proxy config. This means that requests that should not be served with a valid URL within our application will be redirected to go to the WordPress service.Provider
and Consumer
tied to each context you create. To get these two, you can fire off a call to the new createContext
, which accepts a parameter, which will be the default value that can be accessed inside the context. Using context is still a way to bypass intermediate components, when passing data deep down within components, however the new API is way more intuitive and is actually easier to set up than Redux, so let's see how it is done.// Store.js
import React, { createContext, Component } from 'react';
const { Provider, Consumer } = createContext();
<Provider/>
, however we also want to be able to retrieve data, which will manipulate context. For this we'll create a wrapper component, which uses the Provider and can pass down data as well as methods for data retrieval, much like mapDispatchToProps
in redux
.class Store extends Component {
state = {
posts: [],
};
render() {
const {
state: { posts },
props: { children },
} = this;
return <Provider value={{ posts, loadPosts }}>{children}</Provider>;
}
}
<Consumer/>
works by just initializing the state with some sample data and creating the presentational components. Inside the containers
directory we'll create components that make use of the Consumer
's children
prop, which is a function that recieves whatever is the current value
of the Provider
.// containers/PostsContainer.js
// ... imports
const PostsContainer = () => (
<Consumer>{({ posts }) => <PostsView posts={posts} />}</Consumer>
);
export default PostsContainer;
// components/PostsView.js
import React, { Component } from 'react';
class PostsView extends Component {
render() {
const { posts } = this.props;
return posts.length ? (
posts.map(({ title, id, date, slug, excerpt }) => <p>{title.rendered}</p>)
) : (
<div>Loading...</div>
);
}
}
export default PostsView;
// Store.js
class Store extends Component {
state = {
posts: [
{
id: 1,
title: { rendered: 'Hello, HardCoded Data!' },
date: '2018-04-17T00:17:18.040Z',
slug: 'hello-world',
excerpt: { rendered: 'Some random text from the excerpt.' },
},
],
};
// ...
}
// App.js
// ... imports
class App extends Component {
render() {
return (
<Store>
<PostsContainer />
</Store>
);
}
}
export default App;
// Store.js
class Store extends Component {
// ...
loadPosts = () => {
fetch('/wp-json/wp/v2/posts')
.then(res => res.json())
.then(posts => this.setState({ posts }));
};
render() {
const {
state: { posts },
props: { children },
loadPosts,
} = this;
return <Provider value={{ posts, loadPosts }}>{children}</Provider>;
}
}
loadPosts
, which makes a network call and upon recieving data, it will set the state to the new data, which in turn will also update each Consumer
. We also need to add the new method to the provider's values. This is what we'd do when we provide the mapDispatchToProps
parameter of redux-connect
. The final thing we have to do is make use of it in the PostsView
component.// components/PostsView.js
class PostsView extends Component {
componentDidMount() {
this.props.loadPosts();
}
// ...
}
redux-saga
, however we are still coupling all our logic in a single component, which is better than having it in the state of each individual component, but may get clunky with large applications.