1. Preparation
This article is a bit long, so it is recommended to read it patiently. The content of each section is related to the content of the previous section. It is best to go through the case to deepen your memory.
1.1 Create a project
- In the first step, execute the following command to create a React project.
npx create-react-app react-example
cd react-example
- The second step is to install dependencies and run the project
yarn install 或 npm install
yarn start 或 npm run start
1.2 Project structure
As shown in the picture:
1.3 Initialization
src/index.js
Delete the default code of and keep the following part.
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
const root = ReactDOM.createRoot(document.getElementById("root"));
const App = () => {
return <div>Hello</div>
}
root.render(<App />);
Now the project looks like this, a simple one Hello
.
2. The basic usage of React
If you are not familiar with the basic syntax of React, you can read the React & daily grammar I wrote earlier .
1.1 output Hello, world
The first step must be to say Hello, world!
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
const root = ReactDOM.createRoot(document.getElementById("root"));
const App = () => {
return <div>Hello, world!</div> // 改动点
}
root.render(<App />);
1.2 Use of components
组件
It can be created by functions or classes. The aboveApp
function is a component, and HTML & JS code can be mixed in it. React will automatically parse these grammars for us. This kind of writing is called justJSX
.
Below I will define components as a case in a functional way, and class components will demonstrate when talking about the life cycle.
- The first step is to define a component
List
called , and write the content casually, such as Hi List
const List = () => {
return <div>Hi List.</div>
}
- The second step is to introduce
List
. At present, there is only one entranceApp
. Obviously, it can only be placed here.
const App = () => {
return <div>Hello, world! </List> </div>
}
Effect:
1.3 Component nested components
App
Components like nestedList
components, we call this父子组件
, App is the parent component, List is the child component, when the List is placed on the Item component, then the parent component of the Item is List, and the grandparent component is App, and so on.
Now let's List
nest a Item
component for .
const List = () => {
// 多行时要用 () 包裹
return (
<ul>
<Item/> // 引入
</ul>
)
}
const Item = () => {
return <li>item1</li>
}
Effect:
Don't forget that you can also write JSX, now try to use Array.map to loop multiple Item components.
const List = () => {
return (
<ul>
{
[1, 2, 3, 4].map((num) => Item(num) )}
</ul>
)
}
const Item = (num) => {
return <li key={
num}>item{
num}</li>
}
Before looking at the effect, what changes will happen to the above code?
- The Item function component has an extra
num
parameter , which we use to accept and wrap it{num}
in parentheses for access <Item/>
The calling method becomesItem(num)
, because we want to pass the num value to the Item function, so we found another feature, directly declaring without passing parameters<Item/>
will trigger the function.- When looping components,
key
attributes must be included and unique.
Effect:
1.3 Component binding click event
A dry list, how can it work without interactive behavior,
now let’s define an event, when an item is clicked, the corresponding num value will be prompted, the code is as follows:
const onClickItem = (num) => {
alert(num);
}
const Item = (num) => {
return <li key={
num} onClick={
() => onClickItem(num)}>item{
num}</li>
}
explain:
- Click events are expressed in
onClick
camel case, and other events are similar, such as onFocus, onMouse, etc. onClick={() => 函数}
The curly braces return an arrow function, which returns the function we defined, which will be triggered when clicked later.
Don't doubt whether there is a problem with the syntax, even the JSX syntax is there, do you still care about this?
Effect:
1.5 Responsive data
The pop-up window is useless, how about directly changing the item data after clicking?
The question is... how to change? Directly set num = xxx? Friends who are familiar with JS should know that when the num parameter is passed [basic type], it is just a copy, and the original num will be invalid after the change.
Even if it works, how do you change the num in the view? It seems that we still have to rely on the syntax provided by React.
Oh no way, let's learn~
-
The first step
[1,2,3,4]
isuseState
to define and export thearr
original variable , as shown in the figure:
the effect is still the same as before, and there will be no texture here. -
The second step is to declare a
setArr
function . The name can be defined freely, but it is generally better to start with set as a specification, indicating that it is used to change the arr data.
const List = () => {
const [arr, setArr] = useState([1, 2, 3, 4])
// ...省略
- The third step is to trigger
setArr
the function and change the data in the arr array to achieve the view change effect we want.
SincesetArr
is defined in the List function component, other functions cannot be accessed directly, so it has to be passed to the past by passing parameters. In onClickItem, it's a bit convoluted, just let it go, I believe you don't mind (/escape).
Here we focus on this piece of code:
setArr(state => {
const arr = [...state];
arr[1] = 1000;
return arr;
})
After clicking, change arr[1] to 1000, and return the new arr, the effect is as shown in the figure:
You may notice that the state is the original arr, and then we copy it to the new arr with the extension symbol , why do you want to do this? Isn't it okay to state[1] = 1000
just return and go out? It's unnecessary.
It is possible to do this, the data will change, but the view will not change, because React clearly stipulates: state 是不可变数据,你不能直接改变它,而是要用一份副本去改变
.
Why should state adhere to the immutable principle? The official also said that when you want to implement an undo & restore function, there is no way, or is it more complicated to implement? Wouldn’t it be a little unreasonable for React to allow you to go at this time? It’s not the same as eating the last meal and not thinking about the next one.
summary:
- useState can declare reactive data.
- The state data is immutable and should be replaced by a copy, following the immutable principle.
1.6 What is the hook function Hook Function
In fact, we have already used the hook function. The above useState
is a hook function. React has many built-in hooks, such as useEffect, useRef
and so on. I won’t introduce them one by one here, just understand that all the hook functions that start use
with are hook functions.
We can also customize hooks. The question is, what kind of scenarios do we need? What is written in it?
In fact, there is no single answer to this. Everyone has a different understanding of hooks, resulting in thousands of types of hooks .
Personally, I prefer to classify it as 处理脏活
a type of function. More generally, it is used for processing 响应式数据
. For example, there is a functional module for the prize business. For this module, there may be logic for verifying the configuration of prizes. Then I will Several hook functions will be added to this prize for easy subsequent calls.
const usePrize = () => {
const verifyPrizeConfig = (state) => {
// Do something ...
}
const resetPrizeConfig = (state) => {
// Do something ...
}
return [
verifyPrizeConfig,
resetPrizeConfig,
]
}
const [ verifyPrizeConfig ] = usePrize();
1.7 Component life cycle
When each component is rendered, React will trigger some built-in functions step by step. These are called "lifecycle functions". We can do some business processing according to different cycle functions. For example, I want to request the interface to get data before the component is rendered. .
It should be noted here that 函数组件
there is no lifecycle function, only 类组件
one. In this case, we can turn App
this component into a class component, and keep the other things unchanged. Who stipulates 类组件
that it cannot be nested 函数组件
?
- The first step is to change the App function component to a class component, as follows:
// 源 App 函数组件
// const App = () => {
// return <div>Hello, world! <List/> </div>
// }
// 新 APP 类组件
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div>Hello, world! <List /> </div>
}
}
- The second step is to add a lifecycle function. Here, the most common
componentDidMount
function to indicate that the component is triggered in advance when it is rendered for the first time.
class App extends React.Component {
// 省略
componentDidMount() {
// Do something...
alert('Init')
}
}
1.8 Function component simulation life cycle
You may think, this is not fair, why does the function component not have a periodic function? Don't worry, the hook function provided by React useEffect
will come in handy. This hook can completely simulate the three core functions of the life cycle:
componentDidMount() {
} 组件第一次渲染时,就刚刚用到的
componentDidUpdate() {
} 组件数据更新时(state 更新)
componentWillUnMount() {
} 组件销毁时
The way to use it is also very simple. Here we take the List component as an example, and we must not forget our old partner~
- The first step, simulation
componentDidMount
, is as follows:
import {
useState, useEffect } from 'react';
const List = () => {
const [arr, setArr] = useState([1, 2, 3, 4]);
// 模拟 componentDidMount
useEffect(() => {
alert('List init')
});
return (
<ul>
{
arr.map((num) => Item(num, setArr) )}
</ul>
)
}
- The second step, simulation
componentDidUpdate
, listens to the state in the second parameter of useEffect:
// ... 省略
const [arr, setArr] = useState([1, 2, 3, 4])
useEffect(() => {
alert('List init')
}, [arr]) // componentDidUpdate
When clicking to update data, this function will be triggered again, as shown in the figure:
- The third step is to simulate
componentWillUnMount
, just return a function in useEffect.
const [arr, setArr] = useState([1, 2, 3, 4])
useEffect(() => {
alert('List init')
return () => {
// componentWillUnMount
alert('List destroyed!');
};
}, [arr])
When does componentWillUnMount trigger? In fact, whenever the state is updated, the component will be re-rendered, which is a destruction behavior, so when you click to update the data, componentWillUnMount will be triggered first, and then componentDidUpdate will be triggered.
Effect:
Three, React-router-dom
1.1 What is React-router-dom
How can one page be enough? Now we want to click on the item to enter another page without refreshing the page. Here we can use the plug-in React-router-dom provided by React, commonly known as it 路由
.
1.2 Differences between React-router-dom and React-router versions
React-router-dom is a new version based on the transformation of React-router, which is the most commonly used version now React-router-dom
.
In this case, React-router-dom will be used as a demonstration.
1.3 Use of React-router-dom
- The first step is to download react-router-dom.
yarn add react-router-dom
- The second step is to introduce
src/index.js
in .
import {
createBrowserRouter,
RouterProvider,
} from "react-router-dom";
- The third step is to remodel our previous App component introduction method
// 旧代码
// root.render(<App />);
// 新代码
const router = createBrowserRouter([
{
path: "/",
element: <App/>,
},
]);
root.render(<RouterProvider router={
router} />);
Now the effect is the same as before, here are the steps to explain:
- Replace the App in the original root.render with
RouterProvider
the component createBrowserRoute
Introduce App insideelement
.- path (
路由
), the App component will only be rendered when/
the root .
- The fourth step is to create a new
AppDetail
component and mount it/detail
on the route.
const AppDetail = () => {
return <h1>Detail data</h1>
}
const router = createBrowserRouter([
{
path: "/",
element: <App/>,
},
// 挂载到 /detail 路由
{
path: "/detail",
element: <AppDetail/>,
},
]);
- The fifth step, visit
/detail
to see the effect
- The sixth step is to access by clicking and jumping
/detail
, and<Link>
the label .
import {
createBrowserRouter,
RouterProvider,
Link,
} from "react-router-dom";
const Item = (num, setArr) => {
return (
<li key={
num} onClick={
() => onClickItem(num, setArr)}>
<Link to='/detail'>item{
num}</Link>
</li>
)
}
Effect:
react-router-dom is as simple as that. As for the use of other APIs, you can refer to the documentation, and I won’t explain too much here.
4. React-redux
1.1 What is React-redux
React-redux is a tool that can be used to manage the global state state, so that components can access the same state.
1.2 When to use React-redux
When multiple components reuse the same state, you can consider using redux to promote it to the global for easy maintenance.
1.3 React-redux download & configuration
- The first step is to download
react-redux
. The official here also provides a toolkit@reduxjs/toolkit
, which contains all the functions of react-redux and some other built-in functions. It is highly recommended by the official. Download these two together.
yarn add react-redux @reduxjs/toolkit
- The second step is to create a new
store/index.js
file , which is responsible for managing the global state. The initialization content is as follows:
// ./src/store/index.js
import {
configureStore, createSlice } from '@reduxjs/toolkit'
const userSlice = createSlice({
name: 'User', // name 必填的,当前作用域的标识符,可以理解为 nameSpace 命名空间,否则页面上无法正常展示。
initialState: {
// 声明 state 的地方
},
reducers: {
// 声明 reducer 函数的地方
}
});
// 将 store 导出去。
const store = configureStore({
reducer: userSlice.reducer
})
export default store;
- The third step is
src/index.js
to introducestore
import store from './store/index';
import {
Provider } from 'react-redux'
root.render(
<Provider store={
store}>
<RouterProvider router={
router} />
</Provider>
);
explain:
- Import
store
the object . - Import
Provider
the component andRouterProvider
wrap the original component. - will be passed
store
as a parameter toProvider
the component.
At present, the page is no different from the original one, but now we have more functions of redux, why not do it.
1.4 What is Reducer
Reducer
It has almost the same meaning as mutations in Vuex, which specifically defines some functions for processing state. The reducer mainly accepts a state and an action, processes related logic according to these two parameters, and then returns a new state ( ) 遵循前面所说的“不可变原则”
.
1.5 What is dispatch(action)
dispatch
is used to call the reducer function.action
It is a description object to be passed when dispatch calls the reducer function, so that the reducer knows what to do. The description object has two parameters in total:type
/payload
, type is the name of the function that calls the reducer, and payload is the data we want to pass as a parameter for the reducer to accept.
1.6 use
After understanding reducer/dispatch/action
the three core concepts, let's start using:
- The first step is to define global responsive data in
initialState
the object .
// 省略...
initialState: {
// 新增
user: {
name: 'Jack',
desc: 'Hello,world!'
}
},
- The second step is to create a new
User
component , which is used to access the responsive data declared above, and mount it to App and AppDetail for rendering. The code is as follows:
// ./src/index.js
// ...省略
import {
Provider, useSelector } from 'react-redux'
// 新增
const User = () => {
// 用 redux 提供的钩子来获取 state
const user = useSelector(state => state.user);
return (
<div>
<span>{
user.name}</span>
<span> says: {
user.desc}</span>
</div>
)
}
// ./src/index.js
// ...省略
class App extends React.Component {
// ...省略
render() {
return (
<div>
<User /> // 新增:挂载 User
<List />
</div>
)
}
}
const AppDetail = () => {
return (
<h1>
Detail data
<User /> // 新增:挂载 User
</h1>
)
}
Current effect: Jack says: Hello, world!
- The third step is to declare
reducer
the function to change the state data.
// .src/store/index.js
// ...省略
reducers: {
// 声明 reducer 函数的地方
// 新增
changeUserInfo(state, action) {
const {
payload } = action;
switch(payload.state) {
case 'name':
return {
...state,
user: {
...state.user,
name: '杰克',
}
}
case 'desc':
return {
...state,
user: {
...state.user,
desc: '你好,世界!',
}
}
default:
return state;
}
}
}
changeUserInfo function explanation:
- According to payload.state, that is, the data we are going to pass parameters to change the username name or describe
desc - Following the principle of not destroying the state, here we use extensions to merge.
- The fourth step is to use
dispatch(action)
to trigger the reducer to complete the change effect.
// ./src/index.js
import {
Provider, useSelector, useDispatch } from 'react-redux'
const User = () => {
const user = useSelector(state => state.user);
const dispatch = useDispatch(); // 引入触发 reducer 的钩子
return (
<div>
<span>{
user.name}</span>
<span> says: {
user.desc}</span>
// 以下是新增的
<button onClick={
() => dispatch({
type: 'User/changeUserInfo',
payload: {
state: 'name'
}
})}>
更换名字
</button>
<button onClick={
() => dispatch({
type: 'User/changeUserInfo',
payload: {
state: 'desc'
}
})}>
更换描述
</button>
</div>
)
}
Now look at the overall effect:
V. Summary
Unknowingly, we have used React-dom & React-router& React-redux:
root.render( // react-dom
<Provider store={
store}> // react-redux
<RouterProvider router={
router} /> // react-router-dom
</Provider>
);
Congratulations, you have successfully started the React family bucket, and the rest is left to the practice time to help us practice makes perfect.
If you have any questions, please point them out!
over!