React Advance - studiofu/brain GitHub Wiki
Original Component and on top added Spinner Effect
import React from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import CollectionPreview from '../collection-preview/collection-preview.component';
import { selectCollectionsForPreview } from '../../redux/shop/shop.selectors';
import { CollectionsOverviewContainer } from './collections-overview.styles';
const CollectionsOverview = ({ collections }) => (
<CollectionsOverviewContainer>
{collections.map(({ id, ...otherCollectionProps }) => (
<CollectionPreview key={id} {...otherCollectionProps} />
))}
</CollectionsOverviewContainer>
);
const mapStateToProps = createStructuredSelector({
collections: selectCollectionsForPreview
});
export default connect(mapStateToProps)(CollectionsOverview);import React from 'react';
import { SpinnerContainer, SpinnerOverlay } from './with-spinner.styles';
const WithSpinner = WrappedComponent => {
const Spinner = ({ isLoading, ...otherProps }) => {
return isLoading ? (
<SpinnerOverlay>
<SpinnerContainer />
</SpinnerOverlay>
) : (
<WrappedComponent {...otherProps} />
);
};
return Spinner;
};
export default WithSpinner;const CollectionsOverviewWithSpinner = WithSpinner(CollectionsOverview);
<Route
exact
path={`${match.path}`}
render={props => (
<CollectionsOverviewWithSpinner isLoading={loading} {...props} />
)}
/>refer to project lesson-30
need to use generator in which yield keyword is used for async call and effect management in Sagas
load saga function
import createSagaMiddleware from 'redux-saga';
sagaMiddleware.run(rootSaga);load sub saga function
export default function* rootSaga() {
yield all([call(shopSagas), call(userSagas), call(cartSagas)]);
}write sagas function, takeLatest will monitor the action fired and execute the function
use put to dispatch action
async call is handled in sagas function
import { takeLatest, put, all, call } from 'redux-saga/effects';
import UserActionTypes from './user.types';
export function* onSignUpSuccess() {
yield takeLatest(UserActionTypes.SIGN_UP_SUCCESS, signInAfterSignUp);
}
export function* signInAfterSignUp({ payload: { user, additionalData } }) {
yield getSnapshotFromUserAuth(user, additionalData);
}
export function* getSnapshotFromUserAuth(userAuth, additionalData) {
try {
const userRef = yield call(
createUserProfileDocument,
userAuth,
additionalData
);
const userSnapshot = yield userRef.get();
yield put(signInSuccess({ id: userSnapshot.id, ...userSnapshot.data() }));
} catch (error) {
yield put(signInFailure(error));
}
}use hook in functional components to set state
const UseStateExample = () => {
const [name, setName] = useState('Yihua');
const [address, setAddress] = useState('Amsterdam');
return (
<Card>
<h1> {name} </h1>
<h1> {address} </h1>
<button onClick={() => setName('Andrei')}>Set Name to Andrei</button>
<button onClick={() => setAddress('Canada')}>Set Address</button>
</Card>
);
};use effect to trigger function when component change or first time rendering
const UseEffectExample = () => {
const [user, setUser] = useState(null);
const [searchQuery, setSearchQuery] = useState('Bret');
useEffect(() => {
const fetchFunc = async () => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/users?username=${searchQuery}`
);
const resJson = await response.json();
setUser(resJson[0]);
};
fetchFunc();
}, [searchQuery]);
// [searchQuery] array is used to trigger the effect function when the searchQuery state change, empty array [] means to trigger one time at the beginning of rendering
no more connect function, mapStateToProps, mapDispatchToProps
just use useSelector to get the redux store state and use useDispatch to get the dispatch function
refer to redux hook project
https://github.com/quantificial/crwn-hooks
useEffect , call after dom re-render
useLayoutEffect, call before dom re-render
useRef, create a ref to the dom render element, such as div
import { useLayoutEffect, useEffect, useRef } from 'react';
import './styles.css';
export const UseLayoutEffectExample = () => {
const ourDiv = useRef();
useEffect(() => {
console.log('useEffect');
}, [ourDiv]);
useLayoutEffect(() => {
console.log('useLayoutEffect');
ourDiv.current.style.backgroundColor = 'red';
}, [ourDiv]);
return (
<div id='my-div' ref={ourDiv}>
useLayoutEffect vs useEffect
</div>
);
};Provider and Consumer
central store of states and adata
create context
import { createContext } from 'react';
import SHOP_DATA from './shop.data';
const CollectionsContext = createContext(SHOP_DATA);
export default CollectionsContext;use context to get data from context
import React, { useContext } from 'react';
import CollectionPreview from '../collection-preview/collection-preview.component';
import CollectionsContext from '../../contexts/collections/collections.context';
import './collections-overview.styles.scss';
const CollectionsOverview = () => {
const collectionsMap = useContext(CollectionsContext);
const collections = Object.keys(collectionsMap).map(
key => collectionsMap[key]
);
return (
<div className='collections-overview'>
{collections.map(({ id, ...otherCollectionProps }) => (
<CollectionPreview key={id} {...otherCollectionProps} />
))}
</div>
);
};
export default CollectionsOverview;provider and such that Header Component could be able to get the currentUser value through Context
import CurrentUserContext from './contexts/current-user/current-user.context';
render() {
return (
<div>
<CurrentUserContext.Provider value={this.state.currentUser}>
<Header />
</CurrentUserContext.Provider>
Provider Pattern, so the child component could be able to get the context value and use the function
import React, { createContext, useState, useEffect } from 'react';
import {
addItemToCart,
removeItemFromCart,
filterItemFromCart,
getCartItemsCount,
getCartTotal
} from './cart.utils';
export const CartContext = createContext({
hidden: true,
toggleHidden: () => {},
cartItems: [],
addItem: () => {},
removeItem: () => {},
clearItemFromCart: () => {},
cartItemsCount: 0,
cartTotal: 0
});
const CartProvider = ({ children }) => {
const [hidden, setHidden] = useState(true);
const [cartItems, setCartItems] = useState([]);
const [cartItemsCount, setCartItemsCount] = useState(0);
const [cartTotal, setCartTotal] = useState(0);
const addItem = item => setCartItems(addItemToCart(cartItems, item));
const removeItem = item => setCartItems(removeItemFromCart(cartItems, item));
const toggleHidden = () => setHidden(!hidden);
const clearItemFromCart = item =>
setCartItems(filterItemFromCart(cartItems, item));
useEffect(() => {
setCartItemsCount(getCartItemsCount(cartItems));
setCartTotal(getCartTotal(cartItems));
}, [cartItems]);
return (
<CartContext.Provider
value={{
hidden,
toggleHidden,
cartItems,
addItem,
removeItem,
clearItemFromCart,
cartItemsCount,
cartTotal
}}
>
{children}
</CartContext.Provider>
);
};
export default CartProvider;