React - studiofu/brain GitHub Wiki

Projects

Refer to demo-react-monster

state management: https://github.com/pmndrs/zustand

Fundamental

Imperative => handle all the changes actively

Declarative => change based on updated internal state passively

VS Code Setup https://www.udemy.com/course/complete-react-developer-zero-to-mastery/learn/lecture/15462544#overview

babel + webpack during build

babel => convert code to native javascript understood by browser

webpack => to pack all the code into chunk

use "react-scripts eject" to show all the configuration files and could manually handle the compilation

fetch

fetch => return a promise object and promise means to have a value returned, promise object takes a function with resolve and reject as parameters, new Promise((resolve,reject) => resolve("value")) or reject("failure"),

fetch syntax => fetch(url).then(...).then(...).catch(...)

async await syntax => const func = async() => { const responseResult = await fetch(url)... const json = await responseResult.json()... }

async await combination wait until the function process complete before move into next step

setState issue

this.setState({counter: this.state.counter + 1} )

console.log(this.state.counter) <== not expected, the counter is old value

this.setState({counter: this.state.counter + 1}, () => {console.log(this.state.counter} ) <== correct

as setState is async function, callback function is required to use to get the correct value

using prevState and props =>

this.setState((prevState, props) => ({ counter: prevState.counter + 1 }));

this issue

function in class, such as function add(a,b) { return this.state.c + a + b }

"this" is not accessible, need to bind this in the constructor to refer to the current object, such as this.add = this.add.bind(this);

or else use arrow function, such as const add = (a, b) => { return this.state.c + a + b; )

life cycle in class

https://github.com/ZhangMYihua/lifecycles-lesson

https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

componentDidMount / componentDidUpdate / componentWillUnmount

component based

break the ui elements into component and using sass to design the css, need to use node-sass package

refer to https://github.com/quantificial/lesson-3

react router

react-dom, there are big differences between v5 and v6, need to ensure which version needed to use in the development

react-dom : use switch, route, **link **components for the navagation.

the route child's props contains history, location and match objects which are useful for the routing and checking

this.props.history.push('/url')

we won't pass the history props down to the child of child (props tunneling) and use withRouter instead.

**withRouter **is higher order component,for example, export default withRouter(childComponent) so it is able to access history props

firebase integration

refer to https://github.com/quantificial/lesson-11

if using v9, need to use compat

import firebase from 'firebase/compat/app';
import 'firebase/compat/firestore';
import 'firebase/compat/auth';

firebase.initializeApp(config);
export const auth = firebase.auth();
export const firestore = firebase.firestore();

const provider = new firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({ prompt: 'select_account' });
export const signInWithGoogle = () => auth.signInWithPopup(provider);

export default firebase;

redux integration

create store

import { createStore, applyMiddleware } from 'redux';
import logger from 'redux-logger';

import rootReducer from './root-reducer';

const middlewares = [logger];

const store = createStore(rootReducer, applyMiddleware(...middlewares));

export default store;

pass the reducer function to the store and the reducer should be looked like...

import { combineReducers } from 'redux';

import userReducer from './user/user.reducer';

export default combineReducers({
  user: userReducer
});


const INITIAL_STATE = {
  currentUser: null
};

const userReducer = (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case 'SET_CURRENT_USER':
      return {
        ...state,
        currentUser: action.payload
      };
    default:
      return state;
  }
};

export default userReducer;

need to dispatch action to trigger the state update by reducer in store

###need to map action to props.

const mapDispatchToProps = dispatch => ({
  setCurrentUser: user => dispatch(setCurrentUser(user))
});

export default connect(
  null,
  mapDispatchToProps
)(App);

if not mapping the action to props, it is required to call props.dispatch({type: 'ACTION'}) directly

setCurrentUser Action function is just to return the dispatch object

export const setCurrentUser = user => ({
  type: 'SET_CURRENT_USER',
  payload: user
});

map state to props

where state is the state in the store, the currentUser object could be accessed by this.currentUser in the Component object

const mapStateToProps = state => ({
  currentUser: state.user.currentUser
});

export default connect(mapStateToProps)(Component);

sample redux project

refer to https://github.com/quantificial/lesson-21

integrated with redux, used reselect package

need to understand createSelector function (cached the value)

need to understand createStructuredSelector which auto pass the state object to the selector function

const mapStateToProps = createStructuredSelector({
  currentUser: selectCurrentUser
});

or using below, where state is the objects returned from reducer

const mapStateToProps = (state) => ({
  currentUser: selectCurrentUser(state)
});

reducer... so the return object state is state.user and state.cart in mapStateToProps function

import { combineReducers } from 'redux';

import userReducer from './user/user.reducer';
import cartReducer from './cart/cart.reducer';

export default combineReducers({
  user: userReducer,
  cart: cartReducer
});

Local Storage and Redux Persist

need to use redux-persist package and create the persistor object

import { createStore, applyMiddleware } from 'redux';
import { persistStore } from 'redux-persist';
import logger from 'redux-logger';

import rootReducer from './root-reducer';

const middlewares = [logger];

export const store = createStore(rootReducer, applyMiddleware(...middlewares));

export const persistor = persistStore(store);

export default { store, persistStore };

Nested Routing

need to use match object to determine the location and also pass the key to child component

    <Route exact path={`${match.path}`} component={CollectionsOverview} />
    <Route path={`${match.path}/:collectionId`} component={CollectionPage} />
const mapStateToProps = (state, ownProps) => ({
  collection: selectCollection(ownProps.match.params.collectionId)(state)
});

use Stripe

need to create stripe account and use some react components for the payment from npm

use heroku to deploy static react app

refer to https://github.com/mars/create-react-app-buildpack

Remarks

install yarn

npm install --global yarn

update react-scripts

yarn upgrade --latest react-scripts

or using

yarn add react-scripts@latest react-dom@latest react@latest node-sass@latest firebase@latest

https://stackoverflow.com/questions/48727922/how-to-upgrade-a-react-project-built-with-create-react-app-to-the-next-create-re

check: yarn list react react-dom react-scripts

change the dependencies and add ^

Components

React / React Router / Redux / Redux Saga / CSS

Hooks / Context API / GraphQL API

Firebase / Payments - stripe

Redux Saga for handling async call

Quick Start

create application

create-react-app demo-react-app

npm start

sample code for showing how to use state and props

import React from 'react';
import {Component} from 'react';
import logo from './logo.svg';
import './App.css';
import InnerApp from './InnerApp';

class App extends Component {

  state = { name: 'hello tom' }
  test = 10;

  componentDidMount() {
    this.setState({name: 'tom x'});
    this.setState( s => ( {name: `${s.name} chen`}));
  }

  clickMe() {
    alert("clicked");
  }

  render() {
    var {name} = this.state;

    return (
      <div className="App">
        <header className="App-header">
          {name}
          <InnerApp name={name}></InnerApp>
// or using onClick={()=> this.setState({xxx: 'message'})}
          <button onClick={this.clickMe}>click me!</button>
        </header>
       </div>
    )
  }
}

export default App;
import React from 'react';
import {Component} from 'react';

class InnerApp extends Component {
    render() {
        const {name} = this.props;
        return (
            <div>
                InnerApp: {name}
            </div>
        );
    }
}
export default InnerApp;

quick component

import React, { Component } from 'react';
class App extends Component {
  render() {
    return (
      <div>
        <Greeting greeting="Welcome to React" />
      </div>
    );
  }
}
const Greeting = ({ greeting }) => <h1>{greeting}</h1>;
export default App

react hook and functional component

import React, { useState, useEffect } from 'react'

function getTime() {
  const time = new Date()
  const hour = time.getHours()
  const minute = time.getMinutes()
  const second = time.getSeconds()
  return `${hour}${minute}${second}`
}

const HookApp = () => {
  const [ time, setTime ] = useState('')

  // replace componentdidmount and componentwillmount
  useEffect(() => {
    const DateTimeNow = getTime()
    const id = setInterval(() => setTime(DateTimeNow), 1000)
    return () => clearInterval(id)
  })

  return (
    <h4>{time}</h4>
  )
}

export default HookApp;

use Redux configureStore

create the store

store.js
import { configureStore } from '@reduxjs/toolkit';

import favoritesReducer from './favorites';

export const store = configureStore({
  reducer: {
    favoriteMeals: favoritesReducer
  }
});

create the states and dispatch functions

favorites.js
import { createSlice } from '@reduxjs/toolkit';

const favoritesSlice = createSlice({
  name: 'favorites',
  initialState: {
    ids: []
  },
  reducers: {
    addFavorite: (state, action) => {
      state.ids.push(action.payload.id);
    },
    removeFavorite: (state, action) => {
      state.ids.splice(state.ids.indexOf(action.payload.id), 1);
    }
  }
});

export const addFavorite = favoritesSlice.actions.addFavorite;
export const removeFavorite = favoritesSlice.actions.removeFavorite;
export default favoritesSlice.reducer;

get the states and also use the dispatch functions

import { addFavorite, removeFavorite } from '../store/redux/favorites';

const favoriteMealIds = useSelector((state) => state.favoriteMeals.ids);
const dispatch = useDispatch();

dispatch(removeFavorite({ id: mealId }));
dispatch(addFavorite({ id: mealId }));

in the app.js

import { store } from './store/redux/store';
<Provider store={store}>
</Provider>

use Context API

create the provider and create the states and functions

import { createContext, useState } from 'react';

export const FavoritesContext = createContext({
  ids: [],
  addFavorite: (id) => {},
  removeFavorite: (id) => {},
});

function FavoritesContextProvider({ children }) {
  const [favoriteMealIds, setFavoriteMealIds] = useState([]);

  function addFavorite(id) {
    setFavoriteMealIds((currentFavIds) => [...currentFavIds, id]);
  }

  function removeFavorite(id) {
    setFavoriteMealIds((currentFavIds) =>
      currentFavIds.filter((mealId) => mealId !== id)
    );
  }

  const value = {
    ids: favoriteMealIds,
    addFavorite: addFavorite,
    removeFavorite: removeFavorite,
  };

  return (
    <FavoritesContext.Provider value={value}>
      {children}
    </FavoritesContext.Provider>
  );
}

export default FavoritesContextProvider;

use the context

import { FavoritesContext } from '../store/context/favorites-context';

const favoriteMealsCtx = useContext(FavoritesContext);
const mealIsFavorite = favoriteMealsCtx.ids.includes(mealId);
favoriteMealsCtx.removeFavorite(mealId);
favoriteMealsCtx.addFavorite(mealId);

Resources

React Tutorial

https://github.com/shiningjason/react-quick-tutorial

https://auth0.com/blog/react-tutorial-building-and-securing-your-first-app/

⚠️ **GitHub.com Fallback** ⚠️