in this section, we will find out what is a functional component and how to implement it
- The functional component is the alternative way, shorter way to define a component of React
- Go with the functional component, we have many hooks that equivalent to the class’s lifecycle.
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
Rules
- Only Call Hooks at the Top Level inside React function component or custom hook.
- using the
exhaustive-deps
rule as part of oureslint-plugin-react-hooks
package - never add a hook inside a condition/loop because every hook will be load by the order. If any hook disappears after a re-render, some variables will be lost and can not read.
useState(initial value)
- hook into React state and lifecycle features from function components
- states will be initialized in the first time render then only reading after that
- In Class Component this.setState will always lead to render() if shoudComponentUpdate = true.
- In Functional Component after setState React will compare the value by Object.is (only value, can not check the change of object and array)
# EXPERIMENT FOR STATE CHANGE IN FUNCTIONAL COMPONENT1. count is number
a. increasing => re-render
b. same value => no render===> useState DO CHECK the value change to decide re-render or not2. count is object {value: 5}
a. return exactly currentCount => no render
b. return Object.assign({}, currentCount) => re-render
c. return Object.assign(currentCount, {value: 1}) => currentCount.value changed BUT no render
d. setCount multiple time in event-handler => result merge together an return only 1 change===> useState can not compare the Object, Array.
useEffect(callbackFunc, [condTrack1])
- The default behavior for effects is to fire the effect after every completed render. The content effect is always recreated if one of its dependencies changes.
- Some Use Case: Data fetching, setting up a subscription, and manually changing the DOM
- The first param is a function callback. There are 2 parts:
+ the effect no cleanup. it will run when mounted and updated
+ the effect with cleanup (return block). it is similar to componentWillUnMount.
- The second optional param is the tracking condition props/state. IF:
+ no second param. the effect will run if there is any change of props, state.
+ the second param is “[]”. The effect track nothing, so it only runs once time after the first render.
+ the second params is “[stateCount, propTitle]”. The effect will detect the change of “stateCount” and “propTitle” to run. - Could be defined multiple
useLayoutEffect
- Syntax and Logic the same with useEffect, but run synchronously after DOM rendered/updated
- only use when there is a problem with useEffect
useContext(initial value)
- declare the context wants to use in the function using it
- The same with “static themeContext = ThemeContext” in the React Class Component AND need to create context first
const ThemeContext = React.createContext(‘light’);
https://codesandbox.io/s/goofy-tharp-dugd8?file=/src/contexts.js
useRef(initial value)
- Essentially,
useRef
is like a “box” that can hold a mutable value in its.current
property - the same with “React.createRef()” of the React Class Component, but can initial a value.
- The value change doesn't lead to re-render
const myRef = useRef("hello world")
console.log(myRef.current) // => hello world...<input ref={myRef} /> // => now, myRef.current return this element
forwardRef(function
(props, ref))
- is used for forwarding the ref to the chid
- sample code
useImperativeHandle(ref, funcHandle, [deps])
- is used for add/override method of the current prop
- can place in parent or child
- sample code
React.memo(funcComponent), *overrideComparasionFunc(prevProp, nextProp))
- a performance optimization, React uses shallow comparison to detect prop change (only prop)
- is a higher order component. It’s similar to
React.PureComponent
but is used for function components and only for props - the second optional param is the overrideComparasionFunction, it allows we set our own comparison
useMemo(function, [dep1])
- use memory to cache variable FOR OPTIMIZE performance
- only recompute the memoized value when one of the dependencies has changed
- if no dependency in the array, it will be computed on every render (should NEVER USE in this case)
- runs during rendering
const value = useMemo(() => computeExpensiveFunc(a, b), [a, b]);
useCallback(function, [dep1])
- the same with useMemo, but return a function
const callbackFunc = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);// callbackFunc = doSomething
// f()
When we should use memo, useMemo, useCallback?
- Make sure you know exactly what memo, useMemo, useCallback benefit
- Performance optimizations ALWAYS come with a cost but do NOT always come with a benefit. Let’s talk about the costs and benefits of useMemo and useCallback.
- Specifically the cost for
useCallback
anduseMemo
are that you make the code more complex for your co-workers, you could make a mistake in the dependencies array, and you're potentially making performance worse by invoking the built-in hooks and preventing dependencies and memoized values from being garbage collected. Those are all fine costs to incur if you get the performance benefits necessary, but it's best to measure first.
useReducer(reducer, initialArg, *initFunc
)
- the same with redux
- the first param is a function which has switch case with action.type and returns a new value for updating the state
- the second param is the initial value for the state
- the third optional param is the function restruct/handle the input initial value (the second param) then return the new initial value for the state
- the first variable return is the state
- the second variable return is dispatch function which is used for calling to update the state
const initialState = {count: 0};function init(initialValue) {
return Object.assign({name: "Peter"}, initialValue)
}function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState, init);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
Custom Hook
- is just a function that uses other hooks
- If there is no hook inside your function, it just a normal function not a hook
- should name begin ‘use’ to mark it a hook
- state of custom-hook change will lead to the parent call re-render
- sample code
PropTypes
- there are several ways to define
- *. original way like Class (PropTypes from “prop-types”)
- 1. use React.FunctionComponent and typescript
- 2. use React.InferProps, typescript, and PropTypes from “prop-types”
https://codesandbox.io/s/beautiful-borg-7ke6i?file=/src/App.tsx