React Course and Build a Book Store
08 Aug 2017 | React Front-end Coursera JavaScriptUdemy course
1. why redux
-
Application state, store all components’ state, children components get these states by this.props.
-
State container, store application states.
-
Setup:
-
Workflow:
store(create and maintain states)–>
provider(passing down store as props)–>
actions(fire actions to update states)–>
middleware(optional, useful when create a kind of chain of action when the action reaches the user which evaluates what to do with the action)–>
reducers(update the data in the store which then updates the app)–>
store(so on in a cycle)
-
Middleware: hook function
2. setup
-
Install express, webpack, babel
npm init npm i --save express npm i --save webpack npm i --save babel-core babel-loader babel-preset-es2015 babel-preset-stage-1 babel-preset-react npm i --save redux
-
Design file tree:
-- AppFile - node_modulus(folder) - public(folder) - index.html//include bundle.js in script tags - src(folder) - app.js - package.json - server.js - webpack.config.js
-
Write server.js
-
require() function
-
loads a module and assigns it to a variable for your application to use
-
to access functionality located in other files in your project.
-
In Node module system each of your javascript file is a module and can expose functionality to be required by other files.
-
-
Express:
-
Express in nodejs is a framework which provides features like: Middlewares to respond to your request, Routing, Render html web pages.
-
Routing: handle multiple url context in a website, or how they respond to client requests.
-
Route paths can be strings, string patterns, or regular expressions.
-
Middleware functions are functions that have access to the request / response objects, and the next middleware function in the application’s request-response cycle.
-
Serving Static Files: Express provides a built-in middleware express.static to serve static files, such as images, CSS, JavaScript etc
-
-
Using “use strict” directive to make sure you receive an error, writing js need to specify “use strict”, writing jsx, use strict is default
-
So the server.js is like:
"use strict" var express = require('express'); var app = express(); var path = require('path'); //middleware to define folder for strict files or images app.use(express.static('public')); //catch the main route, send back response in index.html app.get('/', function(req,res){ res.sendFile(path.resolve(__dirname,'public','index.html')) }); app.listen(3000,function(){ console.log('listening 3000'); });
-
-
Config webpack, webpack.config.js
-
We make our module available outside using module.exports
-
Watch: true. So that every time we save our changes in one of the files are linked with the app.js, webpack will recompile the bundle file automatically
-
scan all js files, to prevent long compile time, we exclude the old js files inside node_modules
var path = require('path'); const webpack = require('webpack'); module.exports = { //path of our js file; entry: './src/app.js', output: { filename: 'bundle.js', path: path.resolve(__dirname,'public') }, watch: true, module:{ loaders:[ { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', query: { presets: ['react','es2015','stage-1'] } } ] } }
-
-
Start. Start node and webpack, go to localhost:3000
node server.js webpack
3. Create app
-
Import createStore module from redux
-
Create store using reducer
-
Pass the reducers as argument to create store
-
See what the store’s current state, using subscript method
-
Lock the result using store.getState()
-
-
Create and dispatch actions
-
An action is made by an object that has two properties: type and payload
-
Type is a keyword in redux, but payload you can call it as you wish
-
Create a counter :
store.dispatch({type:"INCREMENT", payload:1})
-
-
Define reducers
-
Create reducer by passing two arguments, state and action
-
reducer is used to evaluate what to du when they receive an action
-
-
Try the subscribe method, our listener in app.js
"use strict"; import { createStore } from "redux"; //step 3 define reducers in order to create the store //create reducer by passing two arguments, set the initial value for the state //set initial value to state in function() //the use of reducers are evaluate what to do using switch const reducer = function(state = 0, action) { console.log("reducer...state: ", state, "action: ", action); switch (action.type) { case "INCREMENT": return state + action.payload; break; case "decrement": return state - action.payload; break; } return state; }; //step 1 create the store, pass reducers as parameter //see the current of store, use the subscript method, add listener, const store = createStore(reducer); console.log("store: ", store); store.subscribe(function() { console.log("current state: " + store.getState()); }); //step 2 create and dispatch actions //an action is made by an object that has two properties, type and payload //type, key words in redux, payload, call it as you wish store.dispatch({ type: "INCREMENT", payload: 1 }); store.dispatch({ type: "INCREMENT", payload: 1 }); store.dispatch({ type: "decrement", payload: 1 });
-
When we have complex payload with actions:
"use strict"; import { createStore } from "redux"; //reducer //when payload is an array, state = [], when payload only have one obj, state = {} const reducer = function(state = [], action) { switch (action.type) { case "post_book": return state = action.payload; break; } return state; }; //store,state const store = createStore(reducer); store.subscribe(function() { // console.log("current state: " , store.getState().price); //when the payload is an array: console.log("current state: " , store.getState()[1].price); }); //action store.dispatch({ type:"post_book", payload:[ { id:1, title:'book title', description:'im a book', price:10000 }, { id:2, title:'book title2', description:'im another book', price:10002 } ] });
- CRUD operations, (create, read, update, delete)
const reducer = function(state = {books:[]}, action) { switch (action.type) { case "post_book": //we want add third book in, with out this concat, third book overwrite previous payload, //never use push to concatenate array in redux, use concat method, //because, push is a mutable, in redux, should never mutate the state // let books = state.books.concat(action.payload); // return {books}; return {books:[...state.books,...action.payload]} break; } return state; };
destruct??
-
Pure function, give the same input, always output the same output. Reducer should be pure function.
-
Three principles of redux
-
Single source of truth: the state of whole app is stored in an object tree within a single store(under one object - state)
-
States are read-only: The only way to change the state is to emit an action
-
Changes are made with pure functions: reducers hae to be pure functions
-
-
prevent mute state mutation.
-
Operate arrays: concat(), slice() or …spread operator,
push(),splice() -
Operate objects: Object.Assign() or …spread operator
-
-
Delete
//when all in one file //action, we can write a sequence dispatches of actions store.dispatch({ type:"post_book", payload:[ { id:1, title:'book title', description:'im a book', price:10000 }, { id:2, title:'book title2', description:'im another book', price:10002 } ] }); //we using logger,so we can remove subscribe method const store = createStore(reducers, middleware); store.subscribe(function() { console.log("current state: " , store.getState()); // console.log("current state: " , store.getState().price); // when the payload is an array: // console.log("current state: " , store.getState()[1].price); }); store.dispatch({ type:"post_book", payload:[ { id:3, title:'book title3', description:'im a 3rd book', price:10003 }, ] }); store.dispatch({ type:"delete_book", payload:{id:1}, }); store.dispatch({ type:"update_book", payload: { id:2, title:'book title updated' } }); store.dispatch({ type:"add_to_cart", payload:[{id:1}] }); //reducer //when payload is an array, state = [], when payload only have one obj, state = {} const reducer = function(state = {books:[]}, action) { switch (action.type) { case "post_book": //we want add third book in, with out this concat, third book overwrite previous payload, //never use push to concatenate array in redux, use concat method, //because, push is a mutable, in redux, should never mutate the state // let books = state.books.concat(action.payload); // return {books}; return {books:[...state.books,...action.payload]} break; case "delete_book": const currentBookToDelete = [...state.books]; const indexToDelete = currentBookToDelete.findIndex( // (book)=>{book.id===action.payload.id;} function(book){ return book.id===action.payload.id; } ) console.log("delete",action.payload.id); return {books:[...currentBookToDelete.slice(0,indexToDelete), ...currentBookToDelete.slice(indexToDelete+1)]} break; case "update_book": const currentBookToUpdate = [...state.books]; const indexToUpdate = currentBookToUpdate.findIndex( // (book)=>{book.id===action.payload.id;} function(book){ return book.id===action.payload.id; } ); const bookToUpdate = { ...currentBookToUpdate[indexToUpdate], title:action.payload.title }; console.log("update",bookToUpdate); return {books:[...currentBookToUpdate.slice(0,indexToUpdate),bookToUpdate, ...currentBookToUpdate.slice(indexToUpdate+1)]} break; } return state; };
-
Separate actions and reducers: shopping cart, in one file, easy to understand, but code become hard to maintain, solution: separate into different files, use reducer.combine(),
-
Notice: when import, {}, find the obj in {} from ‘’, without {} means import the obj which is export in ‘’ and name it as ..
- eg:
import reducers from './reducers/index';
vs.import {addToCart} from './actions/cartActions';
name the export object in index as reducer; import addToCart, which is in cartActions.
- eg:
-
Middleware(optional): implement a state logger to save state and show it pretty. we using logger,so we can remove store.subscribe method
-
install:
npm i --save-dev redux-logger
-
add
import { applyMiddleware, createStore } from "redux"; import logger from 'redux-logger'; const middleware = applyMiddleware(logger); const store = createStore(reducers, middleware);
-
2. Create app
-
Install react :
npm i --save react
, and thennpm i --save react-dom
, and ` npm i –save react-redux, and
npm i –save react-router` -
Render a class in html
render( <BooksList />, document.getElementById('app') ) //in class BooksList(bookList.js): class BooksList extends React.Component{ render(){ return ( <div> <h1>hi react-xy</h1> </div> ) } }
-
Redux + React: it will make the redux store available in react, we need the redux component called provider, the provider wraps the entire react app and pass the store as props to react component.
-
Connect react and redux: when pass mapStateToProp as an argument to connect(), component is subscribing to the store, by doing this, returns the updated states to our local component
-
Dispatch actions:
-
export function in bookActions.js
-
deal with action in bookReducer.js
-
connect in component: bookList.js
-
-
Design shopping cart
- react-bootstrap, recommended, translate bootstrap into pure react component
$ npm install --save react-bootstrap
- react-bootstrap, recommended, translate bootstrap into pure react component
-
Architecture
-
main.js
-
menu.js
-
footer.js
-
booksList.js: a list of books, bookItem.js+cart.js
-
booksForm.js
-
about.js
-
contacts.js
-
-
-
Create bookItem component
-
title,
-
description
-
price
-
buy now btn
-
-
Create bookForm component: Add post request
-
Create cart
Note
- virtual DOM, only update the changed element.
- babel, jsx javascript compile engine
- redux framework, flux
Comments