Some introduction

Here’s what this article says:

1. Introduce React Hooks, except useImperativeHandle, useLayoutEffect, useDebugValue

2. Introduce the story

3. Introduction to the Router

4. Create a small demo with the above

Try not to talk about the concept, simple and crude implementation. Already if you already know the idea of componentization, or have used Vue before, it will be easier to read.

References:

1. Official documents

React: Address of station B

3. Every friend who answers my questions

Hooks

UseState and useReducer

Change variables and trigger view updates, but useReducer is better suited for logically complex data updates

const [a,setA] = useState()
Copy the code
 const [state, dispatch] = useReducer(reducer, initialState);
Copy the code

accumulator

Use the useState implementation

const [num, setNum] = useState(0) function addNum() { setNum(num + 1) } return ( <> <p>{num}</p> <button onClick={addNum}>+</button> < / a >)Copy the code

Use useReducer

    const initialNum = 0
    const [num, dispatch] = useReducer(addNum, initialNum)
    function addNum(state, action) {
        console.log(state, action)
        return state + 1
    }
    return (
        <>
            <p>{num}</p>
            <button onClick={dispatch}>+</button>
        </>
    )
Copy the code

Two-way binding

Use the useState implementation

    const [text, setText] = useState("higher~oh~higher")
    function changeText(value) {
        setText(value)
    }
    return (
        <>
            <p>{text}</p>
            <input type="text" onChange={(e) => changeText(e.target.value)} />
        </>
    )
Copy the code

Use useReducer

    const initialText = "higher~oh~higher"
    const [text, setText] = useReducer(changeText, initialText)
    function changeText(_, value) {
        return value
    }
    return (
        <>
            <p>{text}</p>
            <input type="text" onChange={(e) => setText(e.target.value)} />
        </>
    )
Copy the code

useEffect

Perform operations at specific times, such as requesting initial data, destroying data, and listening for data updates

Mock data request

Similar to vUE requesting initial data when a component is loaded.

    const [data, setData] = useState([1, 2, 3])
    const getData = () => {
        return new Promise((res) => {
            setTimeout(() => {
                res([4, 5, 6])
            }, 1000)
        })
    }
    useEffect(() => {
        getData().then((data) => {
            setData(data)
        })
    }, [])
    return (
        <>
            <ul>
                {data.map((i) => {
                    return <li key={i}>{i}</li>
                })}
            </ul>
        </>
    )
Copy the code

Monitoring data updates

useEffect(()=>{
    ...
},[data])
Copy the code

Simulated destruction of data

UseEffect (()=>{return ()=>{// do something before destroying}})Copy the code

useContext

You can invoke the context space from this hook to implement component passing values

createContext

Context Spaces can be created using this API.

CreateContext (Default) Provider: Consume: Consume dataCopy the code
const NumContext = createContext()
return <NumContext.Provider value={{num,setNum}}>
        <Child />
    </NumContext.Provider>
    
function Child(){
    return <NumberContext.Consumer>
            {({num}) => {
                    return <>{num}</>
                }}
        </NumberContext.Consumer>
}
Copy the code

Transfer values between components

Parent-child transmission (or just one layer)

Father – > the son

Return <Child num={123} /> function Child(props){Copy the code

Child -> Parent: The parent component needs to pass a function name to the child component and then implement this function inside its own component; This function is triggered when a child component needs to pass a value.

Const changeNum = ()=>{// changeNum or something else } return <Child changeNum={changeNum} /> function Child(props){return <button onClick={()=>{props.changeNum()}}>change</button> }Copy the code

Contextual values (can be as many levels)

const NumContext = createContext()
return <NumContext.Provider value={{num,setNum}}>
        <Child />
    </NumContext.Provider>
    
function Child(){
    const {num,setNum} = useContext(NumContext)
    return <>
        {num}
    </>
}
Copy the code

useRef

Gets a component or DOM element

Normally in a form element, you can use it

<input value={text} onChange={(e)=>setText(e.target.value)} />
Copy the code

To achieve bidirectional binding.

However, for components that cannot change value, you can use this hook to retrieve some properties of the component

Const el = useRef(null) <input ref={el}/> //Copy the code

UseMemo and useCallback

Prevent child components from being forced to update and consume performance

memo

You can use memo() to wrap the child components, which only works with purely static components

const Child = memo(()=>{
    return <>Child</>
})
Copy the code

useCallback

For some child components that need to interact with the parent component, you can transfer operations to the parent based on the Memo

const Child = memo((props)=>{
    return <button onClick={()=>{props.changeNum()}}>click</button>
})
​
function App(){
    const [num,setNum] = useState(0)
    const changeNum = useCallback(()=>{setNum(num=>num+1)},[])
    return <>
        {num}
        <Child />
    </>
}
Copy the code

useMemo

Pretty much the same as up here

const changeNum = useMemo(()=>{
    return ()=>{
        setNum(num=>num+1)
    }
},[])
Copy the code

React Redux

A state manager designed based on Redux and Context

store

Create a new folder with the same name and use index.js as the entry file.

//src/store/index.js
import { createStore } from "redux"
​
const defaultState = {
    num: 1,
}
​
const reducer = () => {
    return defaultState
}
​
const store = createStore(reducer)
​
export default store
Copy the code

Provider

Typically, the topmost component Provider is given

//src/index.js
import { Provider } from "react-redux"
import store from "./store"
​
ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.getElementById("root")
)
Copy the code

Connect

When obtaining data in a component, connect is used to export data and implement state and Dispatch inside the component. Connect can map both state and Dispatch to properties in props.

//src/App.jsx
import React from "react"
import { connect } from "react-redux"
​
function App(props) {
    return <div>num:{props.num}</div>
}
​
const storeState = (state) => {
    return {
        num: state.num,
    }
}
export default connect(storeState)(App)
Copy the code

Consume

After the above steps, you can use the Store data within the component

function App(props) {
    return <div>num:{props.num}</div>
}
Copy the code

Change the data

  • Add action parameters to reducer and change data according to action.type. If you change state directly, the view will not be updated.
const reducer = (state = defaultState, Parse (json.stringify (state)) if (action.type === "addNum") {// If the type is too large, Switch newstate. num++} return newState}Copy the code
  • In the component, implement Dispatch and put it in the second parameter of Connect
const dispatchStore = (dispatch) => {
    return {
        addNum() {
            let action = { type: "addNum" }
            dispatch(action)
        },
    }
}
export default connect(storeState, dispatchStore)(App)
Copy the code
  • Within the component HTML, dispatch can be called through props
function App(props) { return ( <> <div>num:{props.num}</div> <button onClick={() => { props.addNum() }} > +1 </button> } < / a >)Copy the code

React – Router

router

Create a new folder for routing components and implement a basic route first.

import { BrowserRouter, Route, Routes } from "react-router-dom" import App from ".. /App6" import Home from ".. /pages/Home" const BaseRouter = () => { return ( <BrowserRouter> <Routes> <Route path="/" element={<App />}> </Route> </Routes> </BrowserRouter> ) } export default BaseRouterCopy the code

You want the Home component to render inside the App component

<Route path="/" element={<App />}>
    <Route path="/home" element={<Home />}></Route>
</Route>
Copy the code

The topmost component entry needs to be modified to the form of a route

import Router from "./router/index.jsx"
ReactDOM.render(
    <Router />,
    document.getElementById("root")
)
Copy the code

The parent App sets up outlets to show the child routes

import { Outlet } from "react-router-dom"
export default function App6() {
    return (
        <>
            <Outlet />
        </>
    )
}
Copy the code

jump

After completing the above Settings, start the project and find that the home page is not displayed, you need to manually add /home after the URL to display. This can be done using several hooks.

useLocation

Gets information about the current route, including the path value

const xx = useLocation()
console.log(xx)
Copy the code

useNavigate

The attribute of the receiving route is a parameter, and the route can be redirected

const navigate = useNavigate()
console.log(navigate)
Copy the code

Based on the two hooks, useLocation can be used to obtain the pathname of the current route. If it is the default value, useNavigate is used to jump to the home page.

UseEffect (() = > {/ / perform jumps logic if (pathname = = = "/") {navigate ('/home ')}}, [])Copy the code

This allows you to implement the default redirection in vue-router

Link

Route hops can also be implemented using the Link component

<a href="/home">Back to home </a>Copy the code

The ginseng

If the URL is multilevel, such as /student/10001/report, it needs to be set in the route

<Route path="/student/:id" element={<Student/>}>
    <Route path="/report" element={<Report/>}></Route>
</Route>
Copy the code

Use useParams to get the parameters on the target page

//Student.jsx
const {id} = useParams()
Copy the code

If it’s a query string, like /student? Id =’10001’/report, useSearchParams can be used

const [searchParams, setSearchParams] = useSearchParams()
searchParams.get('id')
Copy the code

UseLocation can be used to obtain state if the navigate jump is used to pass parameters

navigate("/student", { state: { id: '10001' } })
//Student.jsx
const { state } = useLocation()
console.log(state.id)
Copy the code

404

<Route path="*" element={<Error />}></Route>
Copy the code

Small demo

Ok, you have learned enough about React hooks and Redux and routing. So let’s implement a little demo based on what we’ve learned.

So here’s a simple shopping cart, the data in the shopping cart is from Redux, you can click on an item to go to the product details, click the Delete button to delete the data.

Data structure design

Let’s start with the format of the shopping cart data

Car :[{good_id:1, good_name:'aaa', good_price:100, good_pic:url(), count:2}] Goods :[{// good_id:1, good_name:'aaa', good_price:100, good_pic:url(), count:2}] Goods :[{// good_id:1, good_name:'aaa', good_price:100, good_pic:url(), count:2}] Name :'aaa', price:100, PIC: URL (), desc:' This is a blah blah blah '}]Copy the code

A list of components

This is car data.

When importing images, use import instead of writing them directly in SRC

import goodImg from "...."
​
<img src={goodImg}/>
Copy the code

One problem encountered at this stage is that if the child component has the same style name as the parent component, the parent component overrides the child’s style. React does not add scoped to the tag like Vue does, but it can be done by configuring CSS Modules and CSS in JS. If the style relationship is not that complicated, it is fine to simply give the two components different style names.

In this demo, I use the Styled – Components style, implemented as follows

// Provider store={store} <ul> {props.car.map((I) => {return (<MyLi key={i.ID} className="item"> <div ClassName ="item-info"> <p className="title"> Item name: {i.name}</p> <p className="price"> Item price: {i.price}</p> <p className="nums"> ¥{i.count * i.price} </p> </div> <div className="item-tools"> </Button> })} </ul>Copy the code
//assets/css/list.js
import styled from "styled-components"
export const MyLi = styled.li`
    width: 300px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    > div.item-info > .title {
        font-weight: bold;
    }
`
Copy the code

Details of the component

The goods component is displayed

You need good_id to display the details, so change the route to

<Route path="/detail/:id" element={<Detail />}></Route>
Copy the code

In this way, the details of the item with ID 1 can be accessed through /detail/1.

The detailed components are as follows:

function Detail(props) { const { id } = useParams() const [item] = props.goods.filter((i) => i.id === Number(id)) return (< div > < p > name of commodity: {item. The name} < / p > < p > commodity price: {item. Price} < / p > < p > product details: {item. Desc} < / p > < / div >)}Copy the code

If the item is not found, a “removed item” message is displayed. I’m going to use something like v-if+ V-else

item ? (< div > < p > name of commodity: {item. The name} < / p > < p > commodity price: {item. Price} < / p > < p > product details: {item. Desc} < / p > < Link to = "/ home" > Back to home < / Link > < / div >) : (< div > < / div >) under the goods has been}Copy the code

Jump logic

According to my data structure, when I click on an item in car, I get the good_ID, which I pass to the detail component as a parameter. You can then look up goods based on this ID to get product information.

You need to jump to the ID data when you click the view button

const navigate = useNavigate()
    const toDetail = (id) => {
        navigate(`/detail/${id}`)
    }
Copy the code

Remove the logical

The shopping cart data comes from the Store, so all you need to do is trigger a store change

Switch-case can be used to match multiple operations on a store

const reducer = (state = defaultState, action) => { let newState = JSON.parse(JSON.stringify(state)) const { type } = action switch (type) { case "addNum": { newState.num++ break } case "deleteItem": { newState.car = newState.car.filter((i) => i.id ! == action.id) break } default: break } return newState }Copy the code

That’s what happens when the demo is done