React - studiofu/brain GitHub Wiki
Refer to demo-react-monster
state management: https://github.com/pmndrs/zustand
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 => 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
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 }));
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; )
https://github.com/ZhangMYihua/lifecycles-lesson
https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
componentDidMount / componentDidUpdate / componentWillUnmount
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-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
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;
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
});
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);
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
});
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 };
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)
});
need to create stripe account and use some react components for the payment from npm
refer to https://github.com/mars/create-react-app-buildpack
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
check: yarn list react react-dom react-scripts
change the dependencies and add ^
React / React Router / Redux / Redux Saga / CSS
Hooks / Context API / GraphQL API
Firebase / Payments - stripe
Redux Saga for handling async call
create-react-app demo-react-app
npm start
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;
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
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;
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>
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);
https://github.com/shiningjason/react-quick-tutorial
https://auth0.com/blog/react-tutorial-building-and-securing-your-first-app/