Article directory
-
- 1. Project launch: project initialization and configuration
- 2. React and Hook application: implement the project list
- 3. TS Application: JS God Assist - Strong Type
- 4. JWT, user authentication and asynchronous request
- 5. CSS is actually very simple - add styles with CSS-in-JS
- 6. User experience optimization - loading and error state handling
- 7. Hook, routing, and URL state management
- 8. User selector and item editing function
- 9. In-depth React state management and Redux mechanism
Source of learning content: React + React Hook + TS Best Practice - MOOC
Compared with the original tutorial, I used the latest version at the beginning of my study (2023.03):
item | Version |
---|---|
react & react-dom | ^18.2.0 |
react-router & react-router-dom | ^6.11.2 |
antd | ^4.24.8 |
@commitlint/cli & @commitlint/config-conventional | ^17.4.4 |
eslint-config-prettier | ^8.6.0 |
husky | ^8.0.3 |
lint-staged | ^13.1.2 |
prettier | 2.8.4 |
json-server | 0.17.2 |
craco-less | ^2.0.0 |
@craco/craco | ^7.1.0 |
qs | ^6.11.0 |
dayjs | ^1.11.7 |
react-helmet | ^6.1.0 |
@types/react-helmet | ^6.1.6 |
react-query | ^6.1.0 |
@welldone-software/why-did-you-render | ^7.0.1 |
@emotion/react & @emotion/styled | ^11.10.6 |
The specific configuration, operation and content will be different, and the "pit" will also be different. . .
1. Project launch: project initialization and configuration
2. React and Hook application: implement the project list
3. TS Application: JS God Assist - Strong Type
4. JWT, user authentication and asynchronous request
5. CSS is actually very simple - add styles with CSS-in-JS
6. User experience optimization - loading and error state handling
7. Hook, routing, and URL state management
8. User selector and item editing function
9. In-depth React state management and Redux mechanism
1&2
3&4
5. Introduction to redux usage
Predictable state container for JavaScript apps - Predictable state container for JavaScript applications
redux
react
Not directly related to , it can also be used invue
or other js/ts projectsredux
Called a state container is more accurate than a state management tool
Next, let's look at an official case of html + js
using it in ordinaryredux
<!DOCTYPE html>
<html>
<head>
<title>Redux basic example</title>
<script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script>
</head>
<body>
<div>
<p>
Clicked: <span id="value">0</span> times
<button id="increment">+</button>
<button id="decrement">-</button>
<button id="incrementIfOdd">Increment if odd</button>
<button id="incrementAsync">Increment async</button>
</p>
</div>
<script>
function counter(state, action) {
if (typeof state === 'undefined') {
return 0
}
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
var store = Redux.createStore(counter)
var valueEl = document.getElementById('value')
function render() {
valueEl.innerHTML = store.getState().toString()
}
render()
store.subscribe(render)
document.getElementById('increment')
.addEventListener('click', function () {
store.dispatch({
type: 'INCREMENT' })
})
document.getElementById('decrement')
.addEventListener('click', function () {
store.dispatch({
type: 'DECREMENT' })
})
document.getElementById('incrementIfOdd')
.addEventListener('click', function () {
if (store.getState() % 2 !== 0) {
store.dispatch({
type: 'INCREMENT' })
}
})
document.getElementById('incrementAsync')
.addEventListener('click', function () {
setTimeout(function () {
store.dispatch({
type: 'INCREMENT' })
}, 1000)
})
</script>
</body>
</html>
Does it feel very similar to the use-undo I wrote before?
Predictable: For the same input parameters, the return value of the function and its impact are certain
Dos and don'ts in Redux
redux
The instate
isimmutable
, it can only be replaced and cannot be changed. Its design concept is consistent withreact
that in , they all use to compare whether is updated.state
===
state
redux
inreducer
must be a pure function. But this does not mean that asynchronous functions cannot be used, you can use them in the callback of an asynchronous functiondispatch
Why not just return to the original state, but replace it?
Because the only way to compare whether all the attributes in two javascript objects are exactly the same is to do a deep comparison. However, in real applications, the code of a deep comparison is very large, which consumes a lot of performance and requires a lot of comparisons, so a An effective solution is to make a provision that the developer returns a new object whenever any changes occur.
What is a pure function?
- Does not depend on the state of the external environment, only on its input parameters - the same input will always return the same output
- No side effects - does not modify the input values of the function, and does not produce any observable side effects, such as network requests, input and output devices, or data mutations (mutation)
6. react-redux and HoC
Container components are separated from presentation components
7. [Extended Learning] History of React Hook
The following is the original text of the courseware:
History of Hooks
The React team has paid great attention to the code reusability of React from the beginning.
.
Their solutions to code reusability have gone through: Mixin, HOC, Render Prop, until now Custom Hook
.
So Custom Hook is not a product that was born out of nowhere, even if many have rich experience in developing Custom Hook Developers don't know how Hook came about and what role it plays in React
.
If you don't understand this design idea, you can't understand Custom Hook deeply. Let's learn it together today.1. Mixin
var SetIntervalMixin = { componentWillMount: function() { this.intervals = []; }, setInterval: function() { this.intervals.push(setInterval.apply(null, arguments)); }, componentWillUnmount: function() { this.intervals.forEach(clearInterval); } }; var createReactClass = require('create-react-class'); var TickTock = createReactClass({ mixins: [SetIntervalMixin], //使用mixin getInitialstate: function() { return { seconds: 0;}, }, componentDidMount: function() { this.setInterval(this.tick, 1000);//调用mixin上的方法 } })
advantage:
- does serve the purpose of reusing code
shortcoming:
- It's an implicit dependency, and implicit dependencies are considered bad in React
- name conflict problem
- Can only work in ReactcreateClass, does not support ES6 ClassComponent
- In practice, it is found that it is difficult to maintain
.It has been marked as deprecated on the React official website, and the official complaint is here.
2. HOC
Beginning in 2015, the React team announced that Mixin is not recommended, and everyone is recommended to use the HOC mode
.
HOC uses the 'decorator mode' to reuse code:function withWindowWidth(BaseComponent) { class DerivedClass extends ReactComponent { state = { windowWidth: window.innerwidth, } onResize = () => { this.setState({ windowWidth: window.innerwidth, }) } componentDidMount() { window.addEventListener(resizethisonResize) } componentWillUnmount(){ window.removeEventListener(resizethisonResize) } render() { ...} } }
This is where the separation of container presidential and classic container components begins.
.The
following is the most classic case of separation of HOC container components and presentation components - example code of connect in Redux:export const createInfoScreen = (ChildComponent, fetchData, dataName) => { class HOComponent extends Comnonent { state = { counter: 0 } handleIncrementCounter = () => { this.setState({ counter:this.state.counter + 1 }); } componentDidMount(){ this.props.fetchData(); } render() { const { data={ },isFetching, error } = this.props[dataName]; if (isFetching) { return( <div>Loading</div> ); } if (error) { return( <div>Something is wrongPlease tryagain!</div> ... ) } } } }
advantage
- Can work in any component including Class Component
- The principle of separation of container components and display components advocated by it has been achieved: separation of concerns
shortcoming
- unintuitive, difficult to read
- name conflict
- Components are nested layer by layer
3. Render Prop
Since 2017, Render Prop has become popular.
.
Render Prop uses the 'proxy pattern' to reuse code:class WindowWidth extends React.Component { propTypes = { children: PropTypes.func.isRequired } state = { windowWidth: window.innerWidth, } onResize = () => { this.setState({ windowWidth: window.innerWidth, }) } componentDidMount() { window.addEventListener('resize', this.onResize); } componentWillUnmount() { window.removeEventListener('resize', this.onResize); } ... }
React Router also adopts this API design:
<Route path = "/about" render= { (props) => <About { ...props} />}>
advantage:
- flexible
shortcoming:
- hard to read, hard to understand
4. Hook
In 2018, the React team announced a new way to reuse code - React Hook.
.
Its core change is to allow functional components to store their own state. Before that, functional components could not have their own state.
.
This change allows us to abstract the logic in a React component as if it were a normal function.
.Principle
of implementation: closureimport { useState, useEffect } from "react"; const useWindowsWidth = () => { const [isScreenSmall, setIsScreenSmall] = useState(false) let checkScreenize = () => { setIsScreenSmall(window.innerWidth < 600); }; useEffect(()=> { checkscreenSize(); window.addEventListener("resize", checkscreenSize); return () => window.removeEventListener("resize", checkScreenSize); }, []); return isScreenSmall }; export default useWindowsWidth
import React from 'react' import useWindowWidth from'./useWindowWidth.js' const MyComponent = () => { const onSmallScreen = useWindowWidth; return ( // Return some elements ) }
advantage:
- It is very easy to extract the logic out
- very easy to combine
- very readable
- No name conflict issues
shortcoming
- Hook has its own usage restrictions: it can only be used at the top level of the component, and can only be used in the component
- Since the principle is a closure, in rare cases there will be incomprehensible problems
8. Why do we need redux-thunk?
Core source code:
import type {
Action, AnyAction } from 'redux'
import type {
ThunkMiddleware } from './types'
export type {
ThunkAction,
ThunkDispatch,
ThunkActionDispatch,
ThunkMiddleware
} from './types'
/** A function that accepts a potential "extra argument" value to be injected later,
* and returns an instance of the thunk middleware that uses that value
*/
function createThunkMiddleware<
State = any,
BasicAction extends Action = AnyAction,
ExtraThunkArg = undefined
>(extraArgument?: ExtraThunkArg) {
// Standard Redux middleware definition pattern:
// See: https://redux.js.org/tutorials/fundamentals/part-4-store#writing-custom-middleware
const middleware: ThunkMiddleware<State, BasicAction, ExtraThunkArg> =
({
dispatch, getState }) =>
next =>
action => {
// The thunk middleware looks for any functions that were passed to `store.dispatch`.
// If this "action" is really a function, call it and return the result.
if (typeof action === 'function') {
// Inject the store's `dispatch` and `getState` methods, as well as any "extra arg"
return action(dispatch, getState, extraArgument)
}
// Otherwise, pass the action down the middleware chain as usual
return next(action)
}
return middleware
}
export const thunk = createThunkMiddleware()
// Export the factory function so users can create a customized version
// with whatever "extra arg" they want to inject into their thunks
export const withExtraArgument = createThunkMiddleware
- dispatch can be placed in an asynchronous operation
- Using redux-thunk or other middleware can make asynchronous operations as elegant as synchronous operations, and asynchronous and other operations are separated separately
Some reference notes are still in draft stage, so stay tuned. . .