React+Koa Full Stack Development Handbook

Keywords: Javascript React JSON Database npm

Project function

Recently, I was doing a second-hand book trading website, which belongs to the course assignment of B/S architecture, but because of the adoption of a new framework, I am eager to try and record it.

The basic functions of a used book trading website are as follows:

  1. To achieve user registration and login functions, users need to fill in the necessary information and verify when they register, such as user name and password requirements of more than 6 bytes, email format verification, and ensure that the user name and email are unique in the system.
  2. Users can publish books to be traded after login. They need to edit relevant information, including title, original price, sale price, category and content introduction, appearance photos, etc. They can link to the detailed introduction page of external system (such as Amazon/Jingdong/Dangdang) through ISBN and title.
  3. According to the aggregation of books published by users, the home page can be classified and retrieved.
  4. Users can set the transaction mode for sending or offline transactions, and input different contents when generating orders.
  5. Integrating a message system, buyers and sellers can communicate.
  6. Provide purchase module, users can publish the books they want.
  7. Interface styles need to be adapted to PC and mobile browsers.
  8. Implement an Android or iphone client software, with the same function as the website, additional support for positioning function, record location when publishing, according to the location of the user to match the latest book for sale. Messages and orders support push.

Technical Selection

data base

The database is developed using MySQL because the environment has been configured before ()“

back-end

After the comparison between Express and Koa, Koa is chosen as the Web development framework based on Node.js. Koa is a new web framework, built by Express's original team behind the scenes, and uses ES6's new grammar (for example, discarding callback functions and using async to solve asynchronous invocation problems), which looks very elegant o()

Front end

With React+Semantic UI, since there has been enough practice on React before, the focus of this time is still on back-end development and front-end and back-end connections...

development process

Reference Course

Vue+Koa Full Stack Development

Koa Framework Course - Ruan Yifeng

Koa Framework Construction

Initialization

  1. Command line input

    npm init -y
    npm i koa koa-json
    npm i -D nodemon
  2. Change the package.json content to "start" in scripts: "nodemon app. js"

  3. New app.js under the root directory

    const Koa = require("koa");
    const json = require("koa-json");
    const logger = require("koa-logger");
    const KoaRouter = require("koa-router");
    const parser = require("koa-bodyparser");
    
    const app = new Koa();
    const router = new KoaRouter();
    
    // Json Prettier Middleware
    app.use(json());
    app.use(parser());
    app.use(logger());
    
    // Simple Middleware Example
    // app.use(async ctx => (ctx.body = { msg: "Hello world" }));
    
    app.listen(4113, () => console.log("----------Server Started----------"));
    
    module.exports = app;
  4. On the command line, enter node app.js, and the browser opens localhost:3000 to view the returned data.

sequelize Connects to Database

  1. Installation package

    npm install sequelize-auto -g
    npm install tedious -g
    npm install mysql -g
  2. Enter the src directory and enter sequelize-auto-o". / schema"-d bookiezilla-h 127.0.1-u root-p 3306-x XXXXXXX-e mysql. (Following the-o parameter is the output folder directory, -d parameter is the database name, -h parameter is the database address, -u parameter is the database user name, -p parameter is the output folder directory.) After that is the port number, followed by the - x parameter is the database password - e parameter, followed by the database specified as mysql.

    At this point, files for three tables are automatically generated under the schema folder, such as:

    /* jshint indent: 2 */
    
    module.exports = function(sequelize, DataTypes) {
      return sequelize.define(
        "book",
        {
          BookID: {
            type: DataTypes.INTEGER(11),
            allowNull: false,
            primaryKey: true
          },
          BookName: {
            type: DataTypes.STRING(45),
            allowNull: true
          },
          BookCostPrice: {
            type: "DOUBLE",
            allowNull: true
          },
          BookSalePrice: {
            type: "DOUBLE",
            allowNull: true
          },
          BookCategory: {
            type: DataTypes.STRING(45),
            allowNull: true
          },
          BookPhoto: {
            type: DataTypes.STRING(45),
            allowNull: true
          },
          BookContent: {
            type: DataTypes.STRING(45),
            allowNull: true
          },
          BookISBN: {
            type: DataTypes.STRING(45),
            allowNull: true
          }
        },
        {
          tableName: "book"
        }
      );
    };
  3. Create a new file database.js under server src config to initialize the connection between Sequelize and the database.

    const Sequelize = require("sequelize");
    
    // Use url connection to connect, pay attention to changing root: XXXX back to the password of your database
    const BookieZilla = new Sequelize(
      "mysql://root:XXXXX@localhost/bookiezilla",
      {
        define: {
          timestamps: false// Cancel Sequelzie to automatically add timestamps (createdAt and updatedAt) to the data table, otherwise you may get an error when you do add, delete and change checks.
        }
      }
    );
    
    module.exports = {
      BookieZilla // Exposing BookieZilla's interface to facilitate Model calls
    };
  4. In order to query information according to user id conveniently, a data can be added to the database at will.

  5. New file userModel.js under server src models. Connect database with table structure file.

    const db = require("../config/database.js");
    const userModel = "../schema/user.js";// Introducing user's table structure
    const BookieZilla = db.BookieZilla;// Introducing database
    
    const User = BookieZilla.import(userModel);// The import method of sequelize is used to introduce table structure and instantiate User.
    
    const getUserById = async function(id) {
      const userInfo = await User.findOne({
        where: {
          UserID: id
        }
      });
      return userInfo;
    };
    
    module.exports = {
      getUserById,
      getUserByEmail
    };
  6. Create a new file userController.js under server src controllers to execute this method and return the result.

    Koa provides a Context object that represents the context of a conversation (including HTTP requests and HTTP replies). By processing this object, you can control the content returned to the user.

    const user = require("../models/userModel.js");
    
    const getUserInfo = async function(ctx) {
      const id = ctx.params.id;// Get the id in the parameter passed in the url
      const result = await user.getUserById(id);
      ctx.body = result;// Put the result of the request back in the body of response
    };
    
    module.exports = {
      getUserInfo,
      vertifyUserLogin
    };
    
  7. New file auth.js under server src routes is used to plan routing rules under auth.

    const auth = require("../controllers/userController.js");
    const router = require("koa-router")();
    
    router.get("/user/:id", auth.getUserInfo);
    
    module.exports = router;
  8. Go back to app.js in the root directory and "mount" the routing rules onto Koa.

    const Koa = require("koa");
    const json = require("koa-json");
    const logger = require("koa-logger");
    const KoaRouter = require("koa-router");
    const parser = require("koa-bodyparser");
    const auth = require("./src/routes/auth.js");// Introducing auth
    
    const app = new Koa();
    const router = new KoaRouter();
    
    // Json Prettier Middleware
    app.use(json());
    app.use(parser());
    app.use(logger());
    
    // Simple Middleware Example
    // app.use(async ctx => (ctx.body = { msg: "Hello world" }));
    
    // Router Middleware
    router.use("/auth", auth.routes());// Mounted on koa-router, all auth request paths are preceded by'/ auth'request paths.
    
    app.use(router.routes()).use(router.allowedMethods());// Mount routing rules on Koa.
    
    app.listen(4113, () => console.log("----------Server Started----------"));
    
    module.exports = app;
  9. API Test

    SUCCESS!!!

Front-end and back-end data transfer

Because this project uses a front-end and back-end separated architecture, it is necessary to transfer data through json, in order to achieve the login function as an example to illustrate the specific steps of implementation.

Backend Authentication Logon

  1. Ser src models userModel. JS additions for finding users through mailboxes.

    // ...
    const getUserByEmail = async function(email) {
      const userInfo = await User.findOne({
        where: {
          UserEmail: email
        }
      });
      return userInfo;
    };
    
    module.exports = {
      getUserById,
      getUserByEmail
    };
  2. Ser src controller userController. JS adds methods to validate login information and return the results to the front end as json.

    Note that JSON-WEB-TOKEN is actually used here to implement stateless requests. Refer to the principles and implementation methods of jwt. This article and This article.

    Simply put, the login system using JSON-WEB-TOKEN should be as follows:

    • The user enters the account password on the login page and sends the request to the back end with the account password (password encrypted by md5).
    • The back end verifies the user's account and password information, and if it does, sends a TOKEN back to the client. If not, do not send TOKEN back, return validation error message.
    • If the login is successful, the client saves the TOKEN in some way (Session Storage, Local Storage), and then carries the TOKEN in the request Header when requesting other resources.
    • The back end receives the request information, first verifies whether TOKEN is valid, then sends the requested resource if it is valid, and then returns the validation error if it is invalid.

The corresponding libraries need to be installed before use:

npm i koa-jwt jsonwebtoken util -s

In addition, in order to ensure security, the password of the back-end database can not be saved in plaintext, and bcrypt encryption is used here.

npm i bcryptjs -s
const user = require("../models/userModel.js");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcryptjs");

const getUserInfo = async function(ctx) {
  const id = ctx.params.id;
  const result = await user.getUserById(id);
  ctx.body = result;
};

const vertifyUserLogin = async function(ctx) {
  const data = ctx.request.body; // The data from post exists in request.body
  const userInfo = await user.getUserByEmail(data.email);

  if (userInfo != null) { // If this user is not found, null will be returned.
    if (!bcrypt.compareSync(data.psw, userInfo.UserPsw) {
      ctx.body = {
        status: false,
        msg: "Wrong password"
      };
    } else { // If the password is correct
      const userToken = {
        id: userInfo.UserID,
        email: userInfo.UserEmail
      };
      const secret = "react-koa-bookiezilla"; // Specify the key, which is then used to determine token legitimacy
      const token = jwt.sign(userToken, secret); // Issuance of token
      ctx.body = {
        status: true,
        token: token // Return to token
      };
    }
  } else {
    ctx.body = {
      status: false,
      msg: "User doesn't exist"
    };
  }
};

module.exports = {
  getUserInfo,
  vertifyUserLogin
};
  1. Update the routing rules in serversrcroutesauth.js.

    const auth = require("../controllers/userController.js");
    const router = require("koa-router")();
    
    router.get("/user/:id", auth.getUserInfo);
    router.post("/login", auth.vertifyUserLogin);
    
    module.exports = router;

Front-end verifies data and sends requests

The front end mainly uses react-router for routing jump, uses semantic-ui as UI component library, and uses axios to send requests. The Login.js code is as follows:

import React, { Component } from "react";
import {
  Button,
  Form,
  Grid,
  Header,
  Image,
  Message,
  Segment,
  Loader
} from "semantic-ui-react";
import { NavLink, withRouter } from "react-router-dom";
import axios from "axios";
import Logo from "../images/logo.png";

class Login extends Component {
  state = {
    email: "",
    psw: "",
    alert: false,
    load: false
  };

  vertifyFormat = () => {
    var pattern = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
    return pattern.test(this.state.email) && this.state.psw.length >= 6;
  };

  sendLoginRequest = () => {
    if (this.vertifyFormat()) {
      this.setState({
        alert: false,
        load: true
      });
      axios
        .post("/auth/login", {
          email: this.state.email,
          psw: this.state.psw
        })
        .then(res => {
          console.log(res);
        })
        .catch(err => {
          console.log(err);
        });
    } else {
      this.setState({
        alert: true
      });
    }
  };

  render() {
    var alert =
      this.state.alert === false ? (
        <div />
      ) : (
        <Message
          error
          header="Could you check something!"
          list={[
            "Email format must conform to the specification.",
            "Password must be at least six characters."
          ]}
        />
      );
    var load = this.state.load === false ? <div /> : <Loader />;
    return (
      <Grid
        textAlign="center"
        style={{ height: "100vh", background: "#f6f6e9" }}
        verticalAlign="middle"
      >
        <Grid.Column style={{ maxWidth: 450 }}>
          <Header as="h2" color="teal" textAlign="center">
            <Image src={Logo} />
            Log-in to your B::kzilla
          </Header>
          <Form size="large" error active>
            <Segment>
              <Form.Input
                fluid
                icon="user"
                iconPosition="left"
                placeholder="E-mail address"
                onChange={event => {
                  this.setState({
                    email: event.target.value
                  });
                }}
              />
              <Form.Input
                fluid
                icon="lock"
                iconPosition="left"
                placeholder="Password"
                type="password"
                onChange={event => {
                  this.setState({
                    psw: event.target.value
                  });
                }}
              />
              {alert}
              {load}
              <Button
                color="teal"
                fluid
                size="large"
                onClick={this.sendLoginRequest}
              >
                Login
              </Button>
            </Segment>
          </Form>
          <Message>
            New to us?
            <NavLink to="/signup">
              <a href="#"> Sign Up</a>
            </NavLink>
          </Message>
        </Grid.Column>
      </Grid>
    );
  }
}

export default withRouter(Login);

React Configuration Agent

  1. Install http-proxy-middleware.

    npm install http-proxy-middleware -s
  2. The project initialized by create-react-app requires an eject to expose the basic configuration.

    npm run eject
  3. New file setupProxy.js under client SRC to configure proxy to forward information.

    const proxy = require("http-proxy-middleware");
    
    module.exports = function(app) {
      app.use(
        proxy("/api", {
          target: "http://localhost:4113",
          changeOrigin: true
        })
      );
      app.use(
        proxy("/auth", {
          target: "http://localhost:4113",
          changeOrigin: true
        })
      );
    };
  4. Client scripts start. JS is configured in const devServer = new Webpack Dev Server (compiler, server Config), followed by the statement require (". / SRC / setupProxy") (devServer);

  5. The format of the request is as follows:

    axios
      .post("/auth/login", {
        email: this.state.email,
        psw: this.state.psw
      })
      .then(res => {
        console.log(res);
      })
      .catch(err => {
        console.log(err);
      });
  6. Favorite test link!

design principle

data base

User

*UserID UserName UserPsw *UserEmail
INT VARCHAR(45) VARCHAR(45) VARCHAR(45)
CREATE TABLE `bookiezilla`.`user` (
  `UserID` INT NOT NULL,
  `UserName` VARCHAR(45) NULL,
  `UserPsw` VARCHAR(45) NULL,
  `UserEmail` VARCHAR(45) NOT NULL,
  PRIMARY KEY (`UserID`, `UserEmail`));

Book

*BookID BookName BookCostPrice BookSalePrice BookCategory BookPhoto BookContent BookISBN BookRefs
INT VARCHAR(45) DOUBLE DOUBLE VARCHAR(45) VARCHAR(45) VARCHAR(45) VARCHAR(45) VARCHAR(45)
CREATE TABLE `bookiezilla`.`book` (
  `BookID` INT NOT NULL,
  `BookName` VARCHAR(45) NULL,
  `BookCostPrice` DOUBLE NULL,
  `BookSalePrice` DOUBLE NULL,
  `BookCategory` VARCHAR(45) NULL,
  `BookPhoto` VARCHAR(45) NULL,
  `BookContent` VARCHAR(45) NULL,
  `BookISBN` VARCHAR(45) NULL,
  PRIMARY KEY (`BookID`));

Order

*OrderID *UserID *BookID TradeMethod TradeStatus TradeParty TraderID
INT INT INT VARCHAR(45) VARCHAR(45) VARCHAR(45) INT
CREATE TABLE `bookiezilla`.`order` (
  `OrderID` INT NOT NULL,
  `UserID` INT NOT NULL,
  `BookID` INT NOT NULL,
  `TradeMethod` VARCHAR(45) NULL,
  `TradeStatus` VARCHAR(45) NULL,
  `TraderID` INT NULL,
  PRIMARY KEY (`OrderID`));

Front end

directory structure

.
│  .gitignore
│  package-lock.json
│  package.json
│  README.md
│  yarn.lock
│
├─config // Basic Profile
│  │  env.js
│  │  modules.js
│  │  paths.js
│  │  pnpTs.js
│  │  webpack.config.js
│  │  webpackDevServer.config.js
│  │
│  └─jest
│          cssTransform.js
│          fileTransform.js
│
├─public
│      favicon.ico
│      index.html
│      manifest.json
│
├─scripts // File configuration generated after object
│      build.js
│      start.js
│      test.js
│
└─src // Main Pages and Components
    │  App.css
    │  App.js
    │  index.css
    │  index.js
    │  serviceWorker.js
    │  setupProxy.js // Setting up proxy forwarding to solve cross-domain problems
    │
    ├─actions // react-redux needs to define actions
    │      UpdateActions.js
    │
    ├─components // Component section of the page
    │      BookList.jsx
    │      BookMarket.jsx
    │      FeedBack.jsx
    │      OrderInfo.jsx
    │      PublishForm.jsx
    │      SearchBar.jsx
    │      SideMenu.jsx
    │      StatisticData.jsx
    │      StepFlow.jsx
    │
    ├─images // Image resources used in projects
    │      logo.png
    │      matthew.png
    │
    ├─pages // Page section
    │      Home.jsx
    │      Login.jsx
    │      Market.jsx
    │      Message.jsx
    │      Publish.jsx
    │      Signup.jsx
    │
    └─reducers // reducers that react-redux needs to define
            rootReducer.js    

Implementation details

React-router

Reaction-router is used to control routing in the project. The basic principles are as follows:

  1. In App.js, page or component corresponding to routing is introduced, and BrowserRouter, Route and Switch components in react-router-dom are defined.

    // App.jsx
    
    import React, { Component } from "react";
    import { BrowserRouter, Route, Switch } from "react-router-dom";
    
    import SideMenu from "./components/SideMenu";
    import Login from "./pages/Login";
    import Signup from "./pages/Signup";
    import Home from "./pages/Home";
    import Market from "./pages/Market";
    import Publish from "./pages/Publish";
    import Message from "./pages/Message";
    import OrderInfo from "./components/OrderInfo";
    
    class App extends Component {
      render() {
        return (
          <BrowserRouter>
            <div className="App">
              <Switch>
                <Route exact path="/" component={Login} />
                <Route path="/signup" component={Signup} />
                <div>
                  <div>
                    <SideMenu />
                  </div>
                  <div style={{ margin: "10px 10px 10px 160px" }}>
                    {/* Only match one */}
                    <Route path="/home" component={Home} />
                    <Route path="/market" component={Market} />
                    <Route path="/publish" component={Publish} />
                    <Route path="/message" component={Message} />
                    <Route path="/books/:book_id" component={OrderInfo} />
                  </div>
                </div>
              </Switch>
            </div>
          </BrowserRouter>
        );
      }
    }
    
    export default App;
    
  2. When a page Jump is required in a project page, components can be wrapped in withRouter in react-router-dom, and then NavLink can be used to jump.

    // Login.jsx
    
    import { NavLink, withRouter } from "react-router-dom";
    
    class Login extends Component {
      .....
      sendLoginRequest = () => {
        ......
        this.props.history.push("/home");
        render(){
           ......
        }
      };
    
    export default withRouter(Login);
    
React-redux

In this project, react-redux is used for state management. The main function of Redux is to allow the state to be transferred among components with different branches, thus avoiding the problems of data transfer between components with different branches caused by the original method (such as this.props), and the state of parent components can not be modified by sub-components. The specific methods of use are as follows:

  1. The new file rootReducer.js under src reducers is used to update the information in the central state tree.

    // rootReducer.js
    
    const initState = {
      id: null,
      token: null
    };
    
    const rootReducer = (state = initState, action) => {
      if (action.type === "UPDATE_ID") {
        return {
          ...state,
          id: action.id
        };
      }
      if (action.type === "UPDATE_TOKEN") {
        return {
          ...state,
          token: action.token
        };
      }
      return state;
    };
    
    export default rootReducer;
  2. New file UpdateActions.js in src actions is used to define behavior.

    // UpdateActions.js
    
    export const updateId = id => {
      return {
        type: "UPDATE_ID",
        id: id
      };
    };
    
    export const updateToken = token => {
      return {
        type: "UPDATE_TOKEN",
        token: token
      };
    };
  3. In srcindex.js, the component in react-redux is used to wrap the project entry file, and the state tree is built globally.

    // index.js
    
    import React from "react";
    import ReactDOM from "react-dom";
    import "./index.css";
    import App from "./App";
    import * as serviceWorker from "./serviceWorker";
    import "semantic-ui-css/semantic.min.css";
    import { createStore } from "redux";
    import { Provider } from "react-redux";
    import rootReducer from "./reducers/rootReducer";
    
    const store = createStore(rootReducer);
    
    ReactDOM.render(
      <Provider store={store}>
        <App />,
      </Provider>,
      document.getElementById("root")
    );
    
    // If you want your app to work offline and load faster, you can change
    // unregister() to register() below. Note this comes with some pitfalls.
    // Learn more about service workers: https://bit.ly/CRA-PWA
    serviceWorker.unregister();
    
  4. When the information in the state tree needs to be updated, the introduced action is used as a function to update it.

    // Login.jsx
    
    import { connect } from "react-redux";
    import { updateId, updateToken } from "../actions/UpdateActions";
    
    class Login extends Component {
      ......  
      sendLoginRequest = () => {
        ......
        this.props.updateId(res.data.id);
        this.props.updateToken(res.data.token);
        ......
      };  
    }
    
    const mapStateToProps = state => {
      return {};
    };
    
    const mapDispatchToProps = dispatch => {
      return {
        updateToken: token => {
          dispatch(updateToken(token));
        },
        updateId: id => {
          dispatch(updateId(id));
        }
      };
    };
    
    export default connect(
      mapStateToProps,
      mapDispatchToProps
    )(withRouter(Login));
    
  5. When you need to use the information in the state tree, call the connect package component in react-redux first, and then use this.props to call directly.

    // PublishForm.jsx
    
    import { connect } from "react-redux";
    
    class PublishForm extends Component {
        ......
        var UserID = this.props.id;
        var UserToken = this.props.token;
        ......
    }
        
    const mapStateToProps = state => {
      return {
        id: state.id,
        token: state.token
      };
    };
    
    export default connect(mapStateToProps)(PublishForm);

back-end

directory structure

.
│  app.js
│  package-lock.json
│  package.json
│
└─src
    ├─config // Database Configuration
    │      database.js
    │
    ├─controllers // Controller, which fetches request data and calls methods in models to process and return results
    │      apiController.js
    │      msgController.js
    │      userController.js
    │
    ├─models // Instance model, mainly using Sequelize definition method to add, delete and modify the database
    │      bookModel.js
    │      CommentModel.js
    │      orderModel.js
    │      userModel.js
    │
    ├─routes // Routing, different files correspond to different types of api interfaces, which are related to authorization, function realization and information transmission respectively
    │      api.js
    │      auth.js
    │      msg.js
    │
    └─schema // Database table structure, which can be automatically generated using Sequelize
            book.js
            comment.js
            order.js
            user.js

Implementation details

Route mounting

When Koa's back-end listening port receives the request, it will be processed according to the routing rules in app.js. We define different types of interfaces in different files, and then call them through router.use() to avoid interface confusion and complexity.

// app.js

const Koa = require("koa");
const json = require("koa-json");
const logger = require("koa-logger");
const KoaRouter = require("koa-router");
const parser = require("koa-bodyparser");
const auth = require("./src/routes/auth.js");
const api = require("./src/routes/api.js");
const msg = require("./src/routes/msg.js");

const app = new Koa();
const router = new KoaRouter();

// Json Prettier Middleware
app.use(json());
app.use(parser());
app.use(logger());

// Simple Middleware Example
// app.use(async ctx => (ctx.body = { msg: "Hello world" }));

// Router Middleware
router.use("/auth", auth.routes());
router.use("/msg", msg.routes());
router.use("/api", api.routes());

app.use(router.routes()).use(router.allowedMethods());

app.listen(4113, () => console.log("----------Server Started----------"));

module.exports = app;
// auth.js

const auth = require("../controllers/userController.js");
const router = require("koa-router")();

router.get("/user/:id", auth.getUserInfo);
router.post("/login", auth.vertifyUserLogin);
router.post("/signup", auth.signupNewUser);

module.exports = router;
// api.js

const api = require("../controllers/apiController.js");
const router = require("koa-router")();

router.get("/getbooks", api.getAllBooks);
router.get("/getorder/:id", api.getOrderInfo);
router.post("/searchbooks", api.searchBooks);
router.post("/publish", api.publishNewBook);
router.post("/confirmorder", api.updateOrderOfTrade);

module.exports = router;
// msg.js

const msg = require("../controllers/msgController.js");
const router = require("koa-router")();

router.get("/getcomments", msg.getAllComments);
router.post("/newcomment", msg.publishNewComment);

module.exports = router;

Project results

Log in and register

Bookizilla can achieve user registration and user login functions, in which the data needed for user registration is formatted (such as verifying the Email format, ensuring that the two password input data are consistent and not less than 6 bytes). If the user makes an error in the registration process, a corresponding prompt will appear to guide the user to enter correctly.

Login.jsx

Signup.jsx

Personal Home Page

Bookiezilla's home page presents information and data related to the user (such as FAVES, VIEWS, etc.), but mocks and all books published by the user are temporarily used because the back end does not store relevant data.

Home.jsx

Book Market

Bookiezilla's book market presents all the books published by all users. Users can use the search box above to enter keywords (such as title, label, ISBN, etc.). Users can also click the button below the book to see the specific information, and then decide whether to conclude the transaction, or click the link in Amazon to see the book details.

Market.jsx

Book Publishing

Bookiezilla allows users to publish books and set key information about orders (such as book basic information, trading patterns, looking for buyers or sellers, etc.). It should be noted that since a large part of the content of book publishing and book purchasing overlaps, the two are merged here and the TradePart option is given to enable users to choose whether they want to publish or buy books.

Publish.jsx

Information Delivery

Bookiezilla has set up an information publishing panel for communication and information publishing among users. Users can directly post comments or reply to others'comments, so as to have continuous communication.

Message.jsx

https://github.com/Sylvie-Hsu...

Posted by Avochelm on Wed, 14 Aug 2019 02:12:49 -0700