# Hoàn thiện một ví dụ ReactJs Wordpress (ok)

![](/files/-MJIVUewOye3_zo3mjrD)

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">
		  	&copy;&nbsp;footer
	  	</div>
  	</div>
  </footer>
);
export default Footer;
```

{% file src="/files/-MJPQ1VobUvQkw8C\_Cy7" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://learnphp.gitbook.io/learnphp/wordpress-advand/hoan-thien-mot-vi-du-reactjs-wordpress-ok.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
