Hoàn thiện một ví dụ ReactJs Wordpress (ok)
Last updated
Was this helpful?
Last updated
Was this helpful?
C:\xampp\htdocs\abc\wp-content\themes\apps\package.json
{
"name": "apps",
"version": "0.1.0",
"private": true,
"dependencies": {
"@material-ui/core": "^4.11.0",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"bootstrap": "^4.5.2",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.3"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
C:\xampp\htdocs\abc\wp-content\themes\apps\index.php
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="/wp-content/themes/apps/favicon.ico" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="/wp-content/themes/apps/logo192.png" />
<link rel="manifest" href="/wp-content/themes/apps/manifest.json" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
C:\xampp\htdocs\celestial\wp-content\themes\celestial\functions.php
<?php
/**
* Enqueue scripts and styles.
*
* @since Celestial 1.0
*/
function celestial_scripts() {
// Load our main stylesheet.
wp_enqueue_style( 'bootstrap-style', 'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css' );
wp_enqueue_style( 'celestial-style-dist', get_template_directory_uri() . '/dist/style.css');
wp_enqueue_style( 'celestial-style', get_template_directory_uri() . '/style.css' );
// Load scripts
//wp_enqueue_script( 'jquery', 'https://code.jquery.com/jquery-3.2.1.slim.min.js', '20171006', false );
wp_enqueue_script( 'scrollmagic', 'https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.5/ScrollMagic.min.js' , array( 'jquery' ), '1.0', false );
//wp_enqueue_script( 'popper', 'https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js', array( 'jquery' ), '20171006', false );
//wp_enqueue_script( 'bootstrap-script', 'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js', array( 'jquery' ), '20171006', false );
wp_enqueue_script( 'celestial-script', get_template_directory_uri() . '/dist/app.js' , array(), '1.0', true );
$url = trailingslashit( home_url() );
$path = trailingslashit( parse_url( $url, PHP_URL_PATH ) );
wp_scripts()->add_data( 'celestial-script', 'data', sprintf( 'var CelestialSettings = %s;', wp_json_encode( array(
'title' => get_bloginfo( 'name', 'display' ),
'path' => $path,
'URL' => array(
'api' => esc_url_raw( get_rest_url( null, '/wp/v2/' ) ),
'root' => esc_url_raw( $url ),
),
'woo' => array(
'url' => esc_url_raw( 'https://localhost/celestial/wp-json/wc/v2/' ), // hard-code URL since it needs to be HTTPS for WC REST API to work
'consumer_key' => 'ck_20e230a20e7d82952f606e85de9c54af76179722',
'consumer_secret' => 'cs_65ca7ed8948c88ef6ee2c136c010309e6c92e14d'
),
)
)
)
);
}
add_action( 'wp_enqueue_scripts', 'celestial_scripts' );
// Add various fields to the JSON output
function celestial_register_fields() {
// Add Author Name
register_rest_field( 'post',
'author_name',
array(
'get_callback' => 'celestial_get_author_name',
'update_callback' => null,
'schema' => null
)
);
// Add Featured Image
register_rest_field( 'post',
'featured_image_src',
array(
'get_callback' => 'celestial_get_image_src',
'update_callback' => null,
'schema' => null
)
);
// Add Published Date
register_rest_field( 'post',
'published_date',
array(
'get_callback' => 'celestial_published_date',
'update_callback' => null,
'schema' => null
)
);
}
add_action( 'rest_api_init', 'celestial_register_fields' );
function celestial_get_author_name( $object, $field_name, $request ) {
return get_the_author_meta( 'display_name' );
}
function celestial_get_image_src( $object, $field_name, $request ) {
if($object[ 'featured_media' ] == 0) {
return $object[ 'featured_media' ];
}
$feat_img_array = wp_get_attachment_image_src( $object[ 'featured_media' ], 'thumbnail', true );
return $feat_img_array[0];
}
function celestial_published_date( $object, $field_name, $request ) {
return get_the_time('F j, Y');
}
function celestial_excerpt_length( $length ) {
return 20;
}
add_filter( 'excerpt_length', 'celestial_excerpt_length' );
/**
* Add Theme Support
*
* @see https://developer.wordpress.org/reference/functions/add_theme_support/
*/
add_theme_support( 'post-thumbnails' );
C:\xampp\htdocs\abc\wp-content\themes\apps\app.js
! function(e) {
function t(t) { for (var n, p, a = t[0], l = t[1], f = t[2], c = 0, s = []; c < a.length; c++) p = a[c], Object.prototype.hasOwnProperty.call(o, p) && o[p] && s.push(o[p][0]), o[p] = 0; for (n in l) Object.prototype.hasOwnProperty.call(l, n) && (e[n] = l[n]); for (i && i(t); s.length;) s.shift()(); return u.push.apply(u, f || []), r() }
function r() {
for (var e, t = 0; t < u.length; t++) {
for (var r = u[t], n = !0, a = 1; a < r.length; a++) {
var l = r[a];
0 !== o[l] && (n = !1)
}
n && (u.splice(t--, 1), e = p(p.s = r[0]))
}
return e
}
var n = {},
o = { 1: 0 },
u = [];
function p(t) { if (n[t]) return n[t].exports; var r = n[t] = { i: t, l: !1, exports: {} }; return e[t].call(r.exports, r, r.exports, p), r.l = !0, r.exports } p.m = e, p.c = n, p.d = function(e, t, r) { p.o(e, t) || Object.defineProperty(e, t, { enumerable: !0, get: r }) }, p.r = function(e) { "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(e, "__esModule", { value: !0 }) }, p.t = function(e, t) {
if (1 & t && (e = p(e)), 8 & t) return e;
if (4 & t && "object" == typeof e && e && e.__esModule) return e;
var r = Object.create(null);
if (p.r(r), Object.defineProperty(r, "default", { enumerable: !0, value: e }), 2 & t && "string" != typeof e)
for (var n in e) p.d(r, n, function(t) { return e[t] }.bind(null, n));
return r
}, p.n = function(e) { var t = e && e.__esModule ? function() { return e.default } : function() { return e }; return p.d(t, "a", t), t }, p.o = function(e, t) { return Object.prototype.hasOwnProperty.call(e, t) }, p.p = "/wp-content/themes/apps/";
var a = this.webpackJsonpapps = this.webpackJsonpapps || [],
l = a.push.bind(a);
a.push = t, a = a.slice();
for (var f = 0; f < a.length; f++) t(a[f]);
var i = l;
r()
}([])
C:\xampp\htdocs\abc\wp-content\themes\apps\src\index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './containers/App';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
C:\xampp\htdocs\abc\wp-content\themes\apps\src\containers\App\index.js
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import CssBaseline from '@material-ui/core/CssBaseline';
import Container from '@material-ui/core/Container';
import { ThemeProvider } from '@material-ui/styles';
import {CelestialSettings} from './../../constants';
import theme from './../../commons/theme';
import Header from './../../components/Header';
import PostList from './../../components/PostList';
import Post from './../../components/Post';
import ProductList from './../../components/ProductList';
import Product from './../../components/Product';
import Page from './../../components/Page';
import Footer from './../../components/Footer';
class App extends React.Component {
render() {
return (
<React.Fragment>
<ThemeProvider theme={theme}>
<CssBaseline />
<Container maxWidth={false}>
<Router>
<Header />
<Switch>
<Route exact path={CelestialSettings.path} component={PostList} />
<Route exact path={CelestialSettings.path + 'posts/:slug'} component={Post} />
<Route exact path={CelestialSettings.path + 'page/:slug'} component={Page} />
<Route exact path={CelestialSettings.path + 'products'} component={ProductList} />
<Route exact path={CelestialSettings.path + 'products/:product'} component={Product} />
</Switch>
</Router>
<Footer />
</Container>
</ThemeProvider>
</React.Fragment>
);
}
}
export default App;
C:\xampp\htdocs\abc\wp-content\themes\apps\src\constants\index.js
export const CelestialSettings = {
"title": "A B C",
"path": "/",
"URL": {
"api": "http://localhost/celestial/wp-json/wp/v2/",
"root": "https://example.com/"
},
"woo": {
"url": "https://localhost/celestial/wp-json/wc/v2/",
"consumer_key": "ck_20e230a20e7d82952f606e85de9c54af76179722",
"consumer_secret": "cs_65ca7ed8948c88ef6ee2c136c010309e6c92e14d"
}
};
C:\xampp\htdocs\abc\wp-content\themes\apps\src\components\index.js
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import CssBaseline from '@material-ui/core/CssBaseline';
import theme from './../commons/theme';
import Container from '@material-ui/core/Container';
import { ThemeProvider } from '@material-ui/styles';
import Header from './Header';
class App extends React.Component {
render() {
return (
<React.Fragment>
<ThemeProvider theme={theme}>
<CssBaseline />
<Container maxWidth={false}>
<Router>
<Header />
</Router>
</Container>
</ThemeProvider>
</React.Fragment>
);
}
}
export default App;
C:\xampp\htdocs\abc\wp-content\themes\apps\src\components\ProductList\index.js
import React from "react";
import {CelestialSettings} from './../../constants';
import ProductItem from './../ProductItem';
class ProductList extends React.Component {
constructor(props) {
super(props);
this.state = {
products: [],
getProducts: true
};
this.getMoreProducts = this.getMoreProducts.bind(this);
}
componentWillUnmount() {
this.getMoreProducts = null;
}
componentDidMount() {
this.getMoreProducts();
}
getMoreProducts() {
fetch( CelestialSettings.woo.url +"products?consumer_key=" + CelestialSettings.woo.consumer_key + "&consumer_secret=" + CelestialSettings.woo.consumer_secret)
.then(response => {
return response.json();
})
.then(results => {
const allProducts = this.state.products.slice();
results.map(result => {
return allProducts.push({id:result.id, name:result.name, slug:result.slug, description:result.description, images:result.images, price:result.price})
})
this.setState({ products: allProducts });
})
.catch(function(error) {
console.log(
"There has been a problem with your fetch operation: " + error.message
);
});
}
render() {
var {products} = this.state;
var eml = products.map(product => {
return <ProductItem product={product} key={product.id} />;
})
return (
<div className="container post-entry">
<div className="row">
{eml}
</div>
</div>
)
}
}
export default ProductList;
C:\xampp\htdocs\abc\wp-content\themes\apps\src\components\ProductItem\index.js
import React from "react";
import { Link } from "react-router-dom";
import Placeholder from "./../../assets/images/placeholder.jpg";
class PostItem extends React.Component {
render() {
var {product} = this.props;
return (
<article className="col-md-4 card-outer">
<div className="card">
<div className="img-outer">
<Link to={product.slug}>
<img className="card-img-top" src={Placeholder} alt={product.description} />
</Link>
</div>
<div className="card-body">
<h4 className="card-title">
<Link to={product.slug}>{product.name}</Link>
</h4>
<p className="card-text">
<small className="text-warning">$ {product.price}</small>
</p>
<p>{product.description.replace(/<[^>]+>/g, '')}</p>
</div>
</div>
</article>
)
}
}
export default PostItem;
C:\xampp\htdocs\abc\wp-content\themes\apps\src\components\Product\index.js
import React from "react";
import Placeholder from "./../../assets/images/placeholder.jpg";
import { CelestialSettings } from './../../constants';
class Product extends React.Component {
constructor(props) {
super(props);
this.state = {
product: {}
};
}
componentDidMount() {
this.fetchData();
}
fetchData = () => {
const url = window.location.href.split("/");
const slug = url.pop() || url.pop();
const fetchUrl = `${CelestialSettings.woo.url}products?slug=${slug}&consumer_key=${CelestialSettings.woo.consumer_key}&consumer_secret=${CelestialSettings.woo.consumer_secret}`;
fetch(fetchUrl)
.then(response => {
return response.json();
})
.then(res => {
this.setState({
product: res[0]
}
);
});
};
render() {
var {description} = this.state.product;
if(!description) {
description = "";
}
return (
<main className="container">
<div className="row">
<div className="col-sm-4">
<img className="product-image w-100" src={ Placeholder } alt="All thumbnails" />
</div>
<div className="col-sm-8">
<h4 className="card-title">{this.state.product.name}</h4>
<p className="card-text">
<strong>$ {this.state.product.regular_price}</strong>
</p>
<p className="card-text">
<small className="text-muted">
{this.state.product.stock_quantity} in stock
</small>
</p>
<p>{description.replace(/<[^>]+>/g, '')}</p>
</div>
</div>
</main>
)
}
}
export default Product;
C:\xampp\htdocs\abc\wp-content\themes\apps\src\components\PostList\index.js
import React from "react";
import {CelestialSettings} from './../../constants';
import PostItem from "./../PostItem";
class PostList extends React.Component {
constructor(props) {
super(props);
this.state = {
posts: [],
page: 0,
getPosts: true
};
this.getMorePosts = this.getMorePosts.bind(this);
}
componentWillUnmount() {
this.getMorePosts = null;
}
componentDidMount() {
if (this.state.getPosts) {
this.getMorePosts();
}
}
getMorePosts() {
fetch(CelestialSettings.URL.api + "posts")
.then(response => response.json())
.then(results => {
const allPosts = this.state.posts.slice();
results.forEach(single => {
allPosts.push(single);
});
this.setState({ posts: allPosts });
})
.catch(error => {
console.log(
"There has been a problem with your fetch operation: " + error.message
);
});
}
componentDidUpdate() {
}
render() {
return (
<React.Fragment>
<PostItem posts={this.state.posts} />
</React.Fragment>
)
}
}
export default PostList;
C:\xampp\htdocs\abc\wp-content\themes\apps\src\components\PostItem\index.js
import React from "react";
import { Link } from "react-router-dom";
import Placeholder from "./../../assets/images/placeholder.jpg";
class PostItem extends React.Component {
render() {
var {posts} = this.props;
var post = posts.map(post =>{
return (
<article className="col-md-4 card-outer" key={post.id}>
<div className="card">
<div className="img-outer">
<Link to={"posts/" + post.slug}>
<img className="card-img-top" src={ post.fimg_url ? post.fimg_url : Placeholder} alt={post.title.rendered}/>
</Link>
</div>
<div className="card-body">
<h4 className="card-title">
<Link to={"posts/" + post.slug}>{post.title.rendered}</Link>
</h4>
<p className="card-text">
<small className="text-muted">
{post.date_gmt}
</small>
</p>
<p dangerouslySetInnerHTML={{ __html: post.excerpt.rendered }} />
</div>
</div>
</article>
)
})
return (
<div className="container post-entry">
<div className="row">
{post}
</div>
</div>
)
}
}
export default PostItem;
C:\xampp\htdocs\abc\wp-content\themes\apps\src\components\Post\index.js
import React from "react";
import { Link } from "react-router-dom";
import Placeholder from "./../../assets/images/placeholder.jpg";
import { CelestialSettings } from './../../constants';
class Post extends React.Component {
constructor(props) {
super(props);
this.state = {
title: "",
content: "",
slug: "",
auth_name: "",
fimg_url: ""
};
}
componentDidMount() {
this.fetchData();
}
fetchData = () => {
const url = window.location.href.split("/");
const getslug = url.pop() || url.pop();
fetch(CelestialSettings.URL.api + "posts?slug=" + getslug)
.then(response => {
if (!response.ok) {
throw Error(response.statusText);
}
return response.json();
})
.then(res => {
console.log(res);
this.setState({
...this.state,
title: res[0].title.rendered,
content: res[0].content.rendered,
slug: res[0].slug,
auth_name: res[0].auth_name,
fimg_url: res[0].fimg_url
});
});
};
render() {
var { title, content, slug, auth_name, fimg_url } = this.state;
return (
<main className="container">
<div className="row">
<div className="col-lg-12">
<Link to={ slug}>
<img className="card-img-top" src={ fimg_url ? fimg_url : Placeholder} alt={title}/>
</Link>
</div>
<div className="col-lg-12">
<h1>{title}</h1>
<p>{content.replace(/<[^>]+>/g, '')}</p>
</div>
<p className="col-lg-12">
<small className="text-success">
{auth_name}
</small>
</p>
</div>
</main>
)
}
}
export default Post;
C:\xampp\htdocs\abc\wp-content\themes\apps\src\components\Page\index.js
import React from "react";
import { Link } from "react-router-dom";
import Placeholder from "./../../assets/images/placeholder.jpg";
import { CelestialSettings } from './../../constants';
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {
title: "",
content: "",
slug: ""
};
}
componentDidMount() {
this.fetchData();
}
fetchData = () => {
const url = window.location.href.split("/");
const slug = url.pop() || url.pop();
fetch(`${CelestialSettings.URL.api}pages?slug=${slug}`)
.then(response => {
if (!response.ok) {
throw Error(response.statusText);
}
return response.json();
})
.then(res => {
this.setState({
...this.state,
title: res[0].title.rendered,
content: res[0].content.rendered,
slug: res[0].slug,
});
});
};
render() {
var { title, content,slug } = this.state;
return (
<main className="container">
<div className="row">
<div className="col-lg-12">
<Link to={slug}>
<img className="card-img-top" src={ Placeholder} alt={title}/>
</Link>
</div>
<div className="col-lg-12">
<h1>{title}</h1>
<p>{content.replace(/<[^>]+>/g, '')}</p>
</div>
</div>
</main>
)
}
}
export default Page;
C:\xampp\htdocs\abc\wp-content\themes\apps\src\components\Header\index.js
import React from "react";
import { Link } from "react-router-dom";
import {CelestialSettings} from './../../constants';
import 'bootstrap/dist/css/bootstrap.min.css';
const Header = () => (
<div className="container">
<header id="masthead" className="site-header" role="banner">
<nav className="navbar navbar-expand-lg navbar-light ">
<h1 className="site-title">
<Link to={CelestialSettings.path}>Celestial</Link>
</h1>
<button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon" />
</button>
<div className="collapse navbar-collapse" id="navbarNavAltMarkup">
<div className="navbar-nav">
<Link className="nav-item nav-link active" to={CelestialSettings.path}>Home</Link>
<Link className="nav-item nav-link" to={CelestialSettings.path + "page/sample-page"}>Page</Link>
<Link className="nav-item nav-link" to={CelestialSettings.path + "products/"}>Products</Link>
</div>
</div>
</nav>
</header>
</div>
);
export default Header;
C:\xampp\htdocs\abc\wp-content\themes\apps\src\components\Footer\index.js
import React from "react";
const Footer = () => (
<footer className="container mt-5 bg-success">
<div className="row text-center">
<div className="col-md-12">
© footer
</div>
</div>
</footer>
);
export default Footer;