Will it be another year of autumn moves soon? It doesn’t matter! I have here the simplest guide to getting started π
React files are introduced through six brain diagrams. Compared with newcomers, React documents are of great leaps and leaps, and the cases are so comprehensive that it is difficult to read. For this reason, I have compiled a brain map and a simple Demo to help you quickly understand the functions of the ActAPI. The specific Demo address is π react-study-Guide, welcome to starβ. All the brain maps, the Demo, are available in the warehouse
And in many Demo clips, I have recorded GIFs, so that you can more intuitively understand the implementation of the code and the style of the Demo
The overview
Rest assured that although there are many, the content is not complicated. I have provided corresponding explanations for each branch
JSX grammar
Most importantly, inside curly braces {} is JavaScript syntax
- This Demo shows how the React JSX syntax needs to be noticed. Just pay attention to the rules mentioned in the brain map.
<div id="test"></div>
<script type="text/babel">
let data = ['a'.'b'.'c']
Create a virtual DOM
const VDom = (
<div>{/* There must be only one root tag */}<h1 style={{background: 'skyblue', marginLeft: '20px'}} >That's the headline</h1>
<ul className='ul-class-name'>} data.map((item, index) => {return<li key={index}>{item}</li>})}</ul>
</div>
)
ReactDOM.render(VDom, document.querySelector('#test'))
</script>
Copy the code
The React componentization
Componentization is the core content of React. I will break the brain map into parts and look at them one by one
- If you don’t understand
componentization
This word, never mind, keep reading and you will understand it later
Functional and class components
Functional components are by far the most used and popular usage, but one is not superior to the other; usage is up to the individual.
functional
It’s easy to use and doesn’t have the annoying this pointer, which is generally accepted, but you can’t use some of the State, lifecycle functions, and so on in class components (more on that later), but you can hook around this.
function Demo() {
return <h1>Functional Component</h1>
}
ReactDOM.render(<Demo />.document.querySelector('#test'))
Copy the code
The class type
Writing is relatively complex and requires inheriting a Component class exposed via React, but you can use the lifecycle function, state, directly
class Demo extends React.Component {
render() {
return <h1>Class Component</h1>
}
}
ReactDOM.render(<Demo />.document.querySelector('#test'))
Copy the code
- Note if you are not familiar with class writing, I recommend you take a look at the brief introduction to classes at the bottom of this article: How to implement call, bind, new?
The event object
It is all kinds of events (click, etc.), except for writing differences, it is not much different from native.
Writing standards
Use the small hump instead of the traditional way
- The traditional writing
<a herf="#" onclick="console.log('ok! ')"></a>
Copy the code
- React specifies the React event
<a herf="#" onClick={clickFun}></a>
function clickFun () {
console.log('ok! ')}Copy the code
- React prevents event default behavior
<a herf="#" onClick={clickFun}></a>
function clickFun (e) {
e.preventDefault()
console.log('ok! ')}Copy the code
This points to the problem
- In general, since the onClick execution is not synchronized with the function, this points to undefined and needs to be changed within the instance
class Demo extends React.Component {
constructor(props) {
super(props)
this.switchFn = this.switchFn.bind(this) // π is inside the constructor
// Bind this to the current instance of the function
}
switchFn() {
console.log('Click successful')}render(){
return (
<div onClick={this.switchFn}>Here is the event click</div>
)
}
}
ReactDOM.render(<Demo />.document.querySelector('#test'))
Copy the code
- To solve it in the form of arrow functions, if you want to understand why, I suggest you review the properties of arrow functions
class Demo extends React.Component {
constructor(props) {
super(props)
}
switchFn = () = > { / / π
console.log('Click successful') // Change the function to arrow function form
}
render(){
return (
<div onClick={this.switchFn}>Here is the event click</div>
)
}
}
ReactDOM.render(<Demo />.document.querySelector('#test'))
Copy the code
Processor parameter passing
- Solve with bind
<button click={this.switch.bind(this, id)}></button>
Copy the code
- Write it as a callback function
<button click={(e) = > {this.switch(id, e)}}></button>
Copy the code
React conditional rendering and looping
Three properties of a component
Now that we know about the three properties, what would a complete React component look like
State of affairs
The state of the component itself is a property that is mounted on the current component instance
This.setstate (): can be used in two ways:
- Object type
// this.setState((state, props)=>{}, callback)
class Demo extends React.Component {
state = {
name: 'link'
}
switchFn = () = > {
this.setState({ / / π
name: 'kiki'})}render(){
return (
<div onClick={this.switchFn}>
name: {this.state.name}
</div>)}}Copy the code
- functional
// this.setState((state, props)=>{}, callback)
class Demo extends React.Component {
state = {
name: 'link'
}
switchFn = () = > {
this.setState((state, prop) = > { / / π
// Get state and prop directly
name: 'kiki'})}render(){
return (
<div onClick={this.switchFn}>
name: {this.state.name}
</div>)}}Copy the code
- Since setState is executed asynchronously, any processing that needs to be done after setState () needs to be done in Callbak
let name = 'link'
this.setState({name: 'kiki'})
console.log(this.state.name) // We'll get 'link'
Copy the code
- Proper operation
state = {
name: 'link'
}
this.setState({name: 'kiki'}, () = > { / / π
console.log(this.state.name) // get 'kiki'
})
Copy the code
Note: The above is the use of class components. In functional components, the use of State needs to be implemented through the hook. We will explain this in the Hook section
Prop (Property Property)
Props is also a property mounted on the instance, allowing access to all values passed through the tag property, including functions and objects. (component communication), there are also special values that cannot be accessed, such as keys that make unique representations
// Assume this is a component according to JSX syntax
let name = 'link'
<TestComponent name={name} /> // The name is a prop.
// Inside the TestComponent
class TestComponent extends React.Component {
render() {
<p>name: {this.props.name}</p> // π {} braces can write JS syntax}}Copy the code
Ref (reference)
Gets the current DOM element
-
It’s like document.getelementById (‘ ID ‘), except React does it for you
-
use
Note: There are three ways to use ref, but the third is by far the most officially recommended
The value is a string of β (Is not recommended)
// This is a native DOM
export default class index extends Component {
handleClick = () = > {
console.log(this.refs.index); // Get this example
}
render() {
return (
<div className='index' ref='index'>/ / π<h1 ref='index2' onClick={this.handleClick}>index</h1>
</div>)}}Copy the code
Ref callback form
In the form of a callback function that mounts the DOM to the instance (this)
export default class index extends Component {
handleClick = () = > {
console.log(this);
}
render() {
return (
<div className='index' ref={c= >This.input1 = c}> // π<h1 onClick={this.handleClick}>index</h1>
</div>)}}Copy the code
createRef
React creates a ref based on craetRef and stores only one ref. This is officially recommended, but it is relatively difficult to write
- CreatRef () creates a ref manually each time it is used and can only be used by a dedicated person
export default class index extends Component {
headerRef = React.createRef() / / π
divRef = React.createRef()
handleClick = () = > {
console.log(this.headerRef); / / π
console.log(this.divRef);
}
render() {
return (
<div ref={this.divRef} className='index'>/ / π<h1 ref={this.headerRef}onClick={this.handleClick}>index</h1>
</div>)}}Copy the code
Integrated case
Combining event objects with three properties, let’s take a look at a conditional rendering example
-
‘react-study-guide\study-demo\test- demo\ react- componentize \ event object \ conditional rendering.html ‘
-
Relationship between components
Please refer to this flow chart to clarify the relationship
StateDiagram -v2 Demo(Main component) --> LogoutBtn: isLogin === true Demo(Main component) --> Greeting Demo(Main component) --> LoginBtn: isLogin === false Greeting --> Welcome: isLogin === true Greeting --> Bye: isLogin === false
- The effect
That is, there is an isLogin(state) in the global main component that controls the global state. Based on whether or not you’re logged in, what kind of components are we going to show
- The Demo components
class Demo extends React.Component{
state = {
isLogin: true
}
render(){
let button
const { isLogin } = this.state
if(isLogin) {
// Pass the click event function as props to the component
button = <LogoutBtn onLogoutClick={this.logout}/>
} else {
button = <LoginBtn onLoginClick={this.login}/>
}
return (
<div>
<Greeting isLogin={this.state.isLogin}/>{button} {/* Display logoutBtn or LoginBtn */}</div>)}// Control events
login = () = > {
this.setState({isLogin: true})
}
logout = () = >{
this.setState({isLogin: false}}})Copy the code
- Greeting Component (UI component)
It contains two UI sub-components, which UI component is shown according to the state of isLogin. Think about how the main component Demo passes the state of isLogin to the UI component Greeting.
function Greeting(props){
const isLogin = props.isLogin
if(isLogin) return (
<Welcome />
)
return (
<Bye />)}Copy the code
- The UI subcomponents
// UI
function Welcome(){
return <h1>Welcome</h1>
}
function Bye(){
return <h1>Bye</h1>
}
Copy the code
The idea of componentization
If you think about the relationships and functions between the components above, each component is responsible for its own functions, the button component for login and exit events, the UI component for displaying slogans, welcome and bye.
- The same is true in real development. You can improve its reusability by treating what you think makes sense as a component. Componentized development rules are clearer and coupling degree can be effectively reduced.
Take the React website for example:
- We can think of the red part of the header as a header component.
- The orange at the bottom is a component, so we just write an orange component and pass in the title and the content as a prop.
So how do you do that?
/ / data
let arr = [
{
title:'link'.content: 'Ok, I'm going to say something.'
},
{
title:'the nuggets'.content: 'Ok, post.'
},
{
title:'Public Account'.content: 'Ok, be lazy.'},]// Pass the Demo component through prop
ReactDOM.render(<Demo number={arr} />.document.querySelector('#test')) / / π
class Demo extends React.Component {
state = {
arrInfo: this.props.number
}
// Loop through the arR data and store the component in an array
itemList = this.props.number.map((item, index) = > {
return <ItemList item={item} key={index}/>
})
render() {
return <ul>{this.itemList}</ul>}}// UI component, which we loop through above
function ItemList (props) {
let item = props.item
return (
<li>
<div>title: {item.title}</div>
<div>content: {item.content}</div>
</li>)}Copy the code
An orange box is our “information” component. This way we reuse this component instead of copying three of our own. Of course componentization is much more powerful than I describe, the rest is up to your creativity.
Note: I added a key property in the loop, which binds each component to a unique identifier. This helps React identify components. However, this identity is not required to be globally unique, but between sibling nodes, that is, non-sibling nodes (different components), can use an ID in the data as the identity. That’s all you need to know for now. Interest can be in-depth understanding, its role and principle than imagined complex
Component communication
From here, the React component will be written in detail, and these demos are available in the React-Study-Guide project. If you don’t understand, you can clone the project and debug it yourself. It is strongly recommended that you type these demos yourself
Father and son components
It should be obvious from the example above that the parent is passed in via prop.
- Child the parent
Also according to prop, the parent component passes in a function that is passed as an argument when executed within the child component
class Father extends React.Component {
constructor(props) {
super(props)
this.state = {
info: 'No news yet... '
}
}
getInfo = item= > {
this.setState({
info: item
})
}
render(){
return (
<div>
<p>info: {this.state.info}</p>
<Clild getChildInfo={this.getInfo}/>
</div>)}}Copy the code
- Child components
// To get you used to the two component creation methods, I'll switch between them as there are no syntactic issues
function Clild (props) {
function emitInfo() {
props.getChildInfo('I'm the data that the child component sent over.') / / π
}
return (
<button onClick={emitInfo}>Click Send message</button>
)
}
ReactDOM.render(<Father />.document.querySelector('#test'))
Copy the code
Brother components
- State improvement:Originally, two components manage their own state, for example, they each have a property called
title
For both of them to use the sametitle
State, we can taketitle
To the parent component, and then throughprop
The incoming
classDiagram
Father <|-- ChildA
Father <|-- ChildB
class ChildA{
state: "title: 'child'"
}
class ChildB{
state: "title: 'child'"
}
- After state promotion
classDiagram
Father --|> ChildA: title={this.state.title}
Father --|> ChildB: title={this.state.title}
Father: title 'child'
class ChildA{
prop: "title: 'child'"
}
class ChildB{
prop: "title: 'child'"
}
- Here I only provide ideas, because it is consistent with the implementation of the above case, try to implement it yourself!
Not close components
- Redux let’s look at pubsub-JS as a chapter
$ yarn add pubsub-js
pubsub
We don’t use pubsub very much, just to get a sense of it and use it when we need it, but we usually use Redux for common state management, and we can also use renderProp, or higher-order components, or even context, which we’ll talk about later
- Relationship between two components (can have no relationship at all)
export default class App extends Component {
render() {
return (
<div>
<Publish />
<Subscribe />
</div>)}}Copy the code
- Send the components
import React, { Component } from 'react'
import PubSub from 'pubsub-js'
export default class Publish extends Component {
state = {
value: 'I'm a publish to Subscribe'
}
handleValue = (dataType, value) = > {
PubSub.publish('data of publish'.this.state.value) // Send data to π
}
render() {
return (
<div>
<h1>Publish</h1>
<button onClick={this.handleValue}>Click Send data</button>
</div>)}}Copy the code
- The receiving component
import React, { Component } from 'react'
import PubSub from 'pubsub-js'
export default class Subscribe extends Component {
state = {
receivedData: ' '
}
// Subscription data
token = PubSub.subscribe('data of publish'.(msg, data) = > { / / π
this.setState({
receivedData: data
})
})
render() {
return (
<div>
<h1>Subcribe: </h1>
<div>{this.state.receivedData}</div>
</div>)}// Unload the hook and remember to empty the receiver
componentWillUnmount() {
PubSub.unSubscribe(this.token)
}
}
Copy the code
The life cycle
Lifecycle Demo can be cloned to debug the following output example,
A new version of
- When the page is first rendered
- When data is updated
The old version
The life cycle of the older version is demonstrated in the Demo, so I won’t show it here
Pay attention to
The parent component update causes the child component to refresh even if it has nothing to do with the child, which can be resolved by inheriting PureComponent, but shouldComponentUpdate is not available
As you can see from this diagram, the child component has been rerendered even though nothing has changed with the childName I passed in
Comparison of old and new life cycles
Controlled and uncontrolled components
Originally, form elements maintained their own values and could only be modified with user input. In React, mutable state values can only be stored in state and modified based on setState. Combined, the values entered by the user are saved to state and changed based on setState in the event. The state inside the component becomes the only data source (where the values are stored). Components based on this form of control are called “controlled components”.
Look not to understand? See figure GIF
- The top is a controlled component and the bottom is an uncontrolled component
See what the difference is? Data from controlled components is rendered to the page in real time, updating the page and data synchronously. Uncontrolled does not. They both use the change event, the React modified onChange for the controlled component, and the native change event below
- That’s the difference between controlled and uncontrolled. Simple. Of course this is only explained at the view level, so look closely at the top definition
Consider, for example, the controlled components above, how I can ensure that they do not overwrite the same state by requiring only one handler function to handle the event.target.value value.
- React componentize
Can be created as a table of controlled components
React-Router
use
$ yarn add react-router
- Import file can be introduced
import { Route,Link} from 'react-router-dom'
Copy the code
HashRouter and BrowserRouter
In general, BrowserRouter is used to interact with the server, while HashRouter is used for servers that use static files, and both require wrapping around routing
- use
// index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import { BrowserRouter} from 'react-router-dom'
ReactDOM.render(
<BrowserRouter>/ / π package<App />
</BrowserRouter>
, document.getElementById('root'))
Copy the code
Navigation Link
Navigation is an interactive button used to jump to and match the same path by clicking on it. After the link and Route components are introduced, we will look at a complete Route matching
to
- string
<Link to="/goWhere" > goWhere </Link>
Copy the code
- object
<Link to={{
pathname: "/goWhere".search: '? id=1'.hash: '#nav'
}}> goWhere </Link>
Copy the code
NavLink(Link with activation)
Use NavLink default click to add the active class name to the current route. If you need to change the class name, use activeClassName to accept it, which triggers some click effects
<NavLink activeClassName="avtive-name" className="" to="/home" >Home</NavLink>
Copy the code
Routing Component Route
It is responsible for displaying the component that corresponds to the path you passed in the Link’s to property.
- This is a complete route matching Demo.
// App.jsx
import React, { Component } from 'react'
import About from './pages/About'
import Home from './pages/Home'
import { Link, Route} from 'react-router-dom'
import './App.css'
class App extends Component {
render() {
return (
<div className="">
<h1>React-Demo</h1>
<nav className='left'>
<Link to="/about" >About</Link>
<Link to="/home" >Home</Link>
</nav>
<div className='right'>{/* Switch routes */}<Route path='/about' component={About} />{/* π displays the about component if it matches /about */}<Route path='/home' component={Home} />{/* Display the Home component if it matches /about */}</div>
</div>)}}export default App
Copy the code
- This is a basic Route component matching process. Note that I later clicked the browser’s back button, which also retracted my previous page, proving that Route hops are logged into the history stack
- If we replace Link with NavLink it will highlight the active routes based on the class name
rendering
Route components can be passed in three ways: Component, Render, and children
- Notice the Route section
import React, { Component } from 'react'
import About from './pages/About'
import Nav from './pages/Nav'
import { Route,Link} from 'react-router-dom'
import './App.css'
class App extends Component {
refCallback = node= > {
console.log(node)
}
render() {
return (
<div className="">
<h1>React-Demo</h1>
<nav className='left'>
<Link to='/about'>
About
</Link>
<Link to="/home">Home</Link>
<Link to="/nav">Nav</Link>
</nav>
<div className='right'>{/* Switch routes */}<Route path='/about' component={About} /> {/* component */}
<Route path='/home' render={{/ *render* /}props= > (
<div {. props} >Home</div>
)
} />
<Route path='/nav' children={{/ *children* /} ({props.match}) = > (
match ? <Nav {. props} / > : <div>The default</div>
)
} />
</div>
</div>)}}export default App
Copy the code
Notice that we receive a match: Boolean attribute in children, which determines whether the current path matches the /nav path or not. If it doesn’t, it shows the default, and if it does, it shows what we wrote
-
The Render property can conveniently write an inline structure directly, or pass a prop directly
-
Children is only used when you need to display the default content, otherwise similar to Render
switch
Matches only once. If the route is routed through the same path, matches only the one encountered for the first time (improves efficiency).
- Let’s say we have this many routes with the same path then the routes are going to match like this
<div className='right'>
{/* Route switchover */}
<Route path='/about' component={About} />
<Route path='/home' component={Home} />
<Route path='/home' component={Home} />
<Route path='/home' component={Home} />
</div>
Copy the code
- to
Route
Component externalSwitch
The tag prevents this match
import { Route, Link, Switch } from 'react-router-dom'
<div className='right'>
{/* Route switchover */}
<Switch>
<Route path='/about' component={About} />
<Route path='/home' component={Home} /> {/* The match ends */}
<Route path='/home' component={Home} />
<Route path='/home' component={Home} />
</Switch>
</div>
Copy the code
Fuzzy and strict matching
The default is fuzzy matching, which means that if the path is multi-layered, only the first layer will be rendered
class App extends Component {
render() {
return (
<div className="">
<h1>React-Demo</h1>
<nav className='left'>
<Link to='/about'>
About
</Link>
<Link to="/home">Home</Link>
<Link to="/home/a" >HomeA</Link>
</nav>
<div className='right'>{/* Switch routes */}<Route path='/about' component={About} />
<Route path='/home' component={Home} />
<Route path='/home/a' component={HomeA} />{/* Home path */}</div>
</div>)}}Copy the code
- Because of the Router’s fuzzy matching mechanism, if we click on the home component above home/ A, it will also be matched
<div className='right'>
{/* Route switchover */}
<Route path='/about' component={About} />
<Route path='/home' component={Home} exact /> {/* Strictly matches */}
<Route path='/home/a' component={HomeA} /> {/* Home child path */}
</div>
Copy the code
- This can be prevented by adding the exact attribute to the corresponding route
The value of
In general, React resolves the parameters from the path in the form of params
params
Plaintext transmission, but the refresh parameter does not disappear
- The value to be passed is written to the path
<Link to={`/home/cHome/detail/${item.id}/${item.title}`}>{item.title}</Link>
Copy the code
- The value to be passed is written to the path dynamically
<Route path='/home/cHome/detail/:id/:title' component={Detail} />
Copy the code
- And structure it out of props
export default class Detail extends Component {
render() {
const { id, title } = this.props.match.params
let content = msData.find( item= > {
return item.id === id
})
return (
<ul>
<li>content: {content.title}</li>
<li>id: {id}</li>
<li>title: {title}</li>
</ul>)}}Copy the code
search
The value is passed as a question mark parameter without specifying the key in Route’s path, and the refresh does not lose arguments
- The value of
<Link to={`/home/cHome/detail/? id=${item.id}&title=${item.title}`}>{item.title}</Link>
Copy the code
- Receives (urlencoded)
$ yarn add qs
A tripartite library for parsing Urlencoded formats
// in component of Route
const {search} = this.props.location
// Accept an unprocessed string? id=xx&title=xx
const { id, title } = qs.parse(search.substring(1))
Copy the code
query
Non-plaintext transmission, and the refreshed parameters will be lost
- Data is passed in and does not need to be defined in Route
<Link to={{pathname:'/home'.query: totalData}}>Home</Link>
Copy the code
- Receive parameters
class Home extends Component {
render() {
console.log(this.props.location)
const {title, age} = this.props.location.query
return (
<div>
Home: {title}
age: {age}
</div>)}}Copy the code
- Data is lost after the refresh. Procedure
When you need to do some processing on your own to prevent the error from spreading, we will talk about how to prevent the error of the child component from spreading to the parent component in ErrorBoundary
state
Data is transmitted in plaintext, and the data is not lost after refreshing. The method is the same as query, except that the key name is state
<Link to={{pathname:'/home'.query: totalData}}>Home</Link>
Copy the code
class Home extends Component {
render() {
const { state } = this.props.location
return (
<div>
Home:{state}
</div>)}}Copy the code
- The four methods of parameter passing all have their own use scenarios, how to use depends on the individual
Programmatic routing
The use of programmatic routing is very simple, as you don’t need to write a Link component, you can jump the page directly through the function.
- If you’ve ever used an applet or uniapp, this is like navigateTo({})
- It’s very simple to use here, so I’m not going to demonstrate it
Add an event object, and when we click the button we jump
<button onClick={() = > this.push(item.title, item.id)}>push</button>
Copy the code
push (title, id) {
this.props.history.push(`/home/cHome/detail`,{title, id})
}
Copy the code
This completes a jump
Push means to put a history at the top of the browser history stack and jump.
Replace replaces the current record at the top of the stack with the current record. In other words, pressing the back button will not return to the page before the jump.
Forward is the forward key of the browser to jump to a page
Back is the back key of the browser to jump to a page
Lazy-load routes
As with picture lazy loading, it is when we use this routing component that we load. This is a necessary optimization method to improve the rendering speed of the current page. For specific cases, please see the project
lazy
import React, { Component, lazy, Suspense } from 'react'
// Introduce components through lazy functions
const About = lazy(() = > import('./pages/About'))
Copy the code
Suspense
It can accept a component with a fallback property for the transition effect
<div className='right'>
{/* Route switchover */}
<Suspense fallback={<Loading />} > {/* Transition effect component */}
<Route path='/about' component={About} />
<Route path='/home' component={Home} />
</Suspense>
</div>
Copy the code
Redux
Global state management, where states registered to Redux can be obtained directly within any component. Redux is relatively complex to use, and I highly recommend that once you learn the concepts, you Demo the entire Redux to see how it all fits together
What about Redux
For the translation of these modules, purely personal understanding, not unofficial translation
- Actions (instructions)
- Reducers (implementation)
- Store(Stores the result of the execution of the action)
How to understand?
Imagine that you are depositing money at an ATM in the bank. You “Components” click the deposit command and put the money “data” into the machine. The ATM (Store) that receives your Actions will add Reducers to your savings, which is to follow your Actions
-
You can’t get the property directly from store[propName], you have to go to getState()
-
You can get your money at ATMs all over the country (public status)
use
Let’s see what a complete Redux process looks like, based on how we deposit money in the bank
Store(Stores the result of the execution of the action)
To use Redux, you need to create a Store instance to distribute and store data. That is, there should be a store. Js entry file
- So this step is like, if you’re going to preset what the ATM is going to do, countReducer is the preset file, and you put it in the Store and you tell it what it’s going to do.
// store.js
import { createStore } from "redux";
// Need to let Redux know how to follow your instructions from the start
import countReducer from './count-reducers'
export default createStore(countReducer)
Copy the code
APIs
- Store. Dispatch (action) Obtain an action and notify the Reducer to execute it
Actions we are introducing the
- Store. Subscribe listens for State and is executed immediately if State changes, notifying the view layer of updates (rerender)
Since we only change the state in the Store, this may cause some views on the page to change based on the data, but it is not initiated, the following way of listening is very important
// Project entry file
import React from 'react'
import ReactDom from 'react-dom'
import App from './App'
import store from './redux/store'
ReactDom.render(<App />.document.getElementById('root'))
store.subscribe( () = > {
ReactDom.render(<App />.document.getElementById('root'))})Copy the code
- Store.getstate The state cannot be directly obtained externally. You need to obtain the state through the function store.getState()
Generally speaking, in an enterprise project, store and Actions are written in a separate file, so the following code follows this custom
Actions (instructions)
- A valid Action object:
action = {
type: 'whatWillDo'.payload: data
}
Copy the code
Command file
// action.js redux Action, a separate file management
import {INCREMENT, DECREMENT} from './constant'
export const incrementAction = data= > ({type: INCREMENT, data})
export const decrementAction = data= > ({type: DECREMENT, data})
Copy the code
use
Actions need to be distributed through store exposed Dispatches ([object])
import React, { Component } from 'react'
import store from '.. /.. /redux/store'
import { incrementAction, decrementAction} from '.. /.. /redux/count_action'
export default class Header extends Component {
increament = (value) = > {
store.dispatch(incrementAction(value))
}
}
Copy the code
- Of course, if you don’t want to create an Actions file, you can
Action objects you pass in can be accepted as long as they are legal
export default class Header extends Component {
increament = (value) = > {
store.dispatch({type: 'increament'.data: value})
}
}
Copy the code
The Action of asynchronous
The component needs to go through
/ / asynchronous
export const addActionAsync = (data, time) = > {
return (dispatch) = > {
setTimeout(() = > {
dispatch({type: 'add'.payload: data})
}, time)
}
}
Copy the code
Here we return a function that is not a regular Action object. Normal use of this Action will result in an error because Redux’s Reducers cannot accept a function as an Action
- How to solve it?
We need to use a middleware called Thunk in store.js
$ yarn add redux-thunk
To use middleware in store, you pass in a function called applyMiddleware() provided by Redux
import { createStore, applyMiddleware } from "redux";
import countReducer from './count-reducers'
import thunk from 'redux-thunk'
export default createStore(countReducer, applyMiddleware(thunk))
Copy the code
Reducers (implementation)
Reducers, which should be translated as accumulator from its name, is used to process the current state based on the previous state
requirements
-
In the store.js file, you need to pass in the reducers through createStore() to tell Redux what you need to do when you receive an action command
-
Require reducers to be a pure function
use
- Define an initial state. When passed to the Reucer function, the project initialization defines an associated state
const initState = 0
export default function addReudcer (prevState = initState, action) {
const { type, payload } = action
switch (type) {
case 'add':
return prevState + payload
default:
return prevState // (when initialized, no instructions, return default values)}}Copy the code
- in
store.js
Defined in the
import { createStore, applyMiddleware } from "redux";
import addReducer from "./reducers/addReducer";
import thunk from 'redux-thunk'
export default createStore(addReducer, applyMiddleware(thunk))
Copy the code
Enterprises,
If you are familiar with vuex-State you must be wondering why there is no file similar to state.js for managing global State
When a Reducer is exposed using the Reducer alone, the return value is the current state because there is only one value in the state.
However, if we use a large Reducer, we usually need to centralize the exposure and define a key name for each Reducer return value.
- Concentrate the files from the Reducer
// allReducers.js
import { combineReducers } from "redux";
import addReudcer from "./addReducer";
// Merge all the reducers
export default combineReducers({
add: addReudcer
})
Copy the code
- It comes in the same way
allReducers
import { createStore, applyMiddleware } from "redux";
import allReducers from "./reducers/allReducers";
import thunk from 'redux-thunk'
export default createStore(allReducers, applyMiddleware(thunk))
Copy the code
Take a look at the simplest code for redux_Demo, where we use Redux directly within the UI component, but this is actually not in the specification
Connect (Specification)
Using Redux normally requires that we strip operations on store state out of the UI component and pass in operation functions and state values in the form of prop. The UI component is only for presentation
If you had to write a parent component every time, it would be too much trouble. Redux does this for us, too, and will be able to abbreviate this step by executing the function immediately with Connect
use
- Let’s say we have one
ComponentA
component
class ComponentA extends Component {
render() {
return (
<div className='C-A'>
<h1>ComponentA</h1>
</div>)}}Copy the code
- Create a parent container (you don’t have to create it yourself) and pass in the UI component. The immediate function returns the parent component by default, which is the component we use, so we want to expose it so it can be used externally
export default connect()(ComponentA)
Copy the code
How does a parent component pass values to a child component?
Connect (mapStateToProps: function, mapDispatchToProps: function) accepts two parameters.
- The mapStateToProps(state) function is received by default
store.state
Object, let’s say we need to get a state called Add
This function is executed when passed connect and returns the value as props to the UI component
const mapStateToProps = (state) = > { return { add: state.add } }
Copy the code
- MapDispatchToProps (Dispatch) received one
store.dispatch
Function to perform the action
This function is executed when passed connect and returns the value (function) as props to the UI component
const mapDispatchToProps = (dispatch) = > {
return {
addCount: value= > dispatch(addAction(value)),
}
}
Copy the code
- Pass it to Connect, and we’ve created a complete parent component
export default connect( mapStateToProps, mapDispatchToProps )(ComponentA)
Copy the code
You might be surprised to see that in the [mapStateToProps] [mapDispatchToProps] parameter, we are using the state and dispatch functions in the store object. Where did we get these values? Of course we passed store in as prop ourselves,
- Let’s see how to use this component. It’s very simple.
import React, { Component } from 'react'
import ComponentA from "./containers/ComponentA";
import store from './redux/store'; // Import the store object
export default class App extends Component {
render() {
return (
<div>
<ComponentA store={store}/>{/* Pass store to */}</div>)}Copy the code
- How are subcomponents used?
Just like using data/functions passed by prop, there is no difference if you want to see the full code π redux_ stripped
Short for the connect parameter
- MapDispatchToProps actually allows it to be passed as an object
- Also, conncet can do dispatches that we would otherwise have to do ourselves by simply dropping in the action as a key-value pair of an object
Abbreviations:
export default connect(
(state) = > {
return {
state
}
},
(dispatch) = > {
return {
addCount: (value) = > dispatch(addAction(value)),
addCountAsync: (value) = > dispatch(addActionAsync(value)),
}
}
)(ComponentA)
Copy the code
After the abbreviations:
export default connect(
state= > ({ state }),
{
addCount: addAction,
addCountAsync: addActionAsync,
}
)(ComponentA)
Copy the code
No need to pass store.js one by one
Not every such component needs to be passed into the store itself
// index.js entry file
import React from 'react'
import ReactDom from 'react-dom'
import App from './App'
import store from './redux/store'
import { Provider } from 'react-redux' / / π
ReactDom.render(
<Provider store={store}>
<App />
</Provider>.document.getElementById('root'))
// You don't need to listen on your own
// store.subscribe(()=>{
// ReactDom.render(
, document.getElementById('root'))
// })
Copy the code
Redux visualization plug-in
Easier to observe the flow of data
$ yarn add redux-devtools-extension
// store.js
import { createStore, applyMiddleware } from "redux";
import thunk from 'redux-thunk'
import { composeWithDevTools } from "redux-devtools-extension";
import allReducer from "./reducers";
export default createStore(allReducer, composeWithDevTools(applyMiddleware(thunk)))
Copy the code
- Google Chrome installs a plugin
- The completion of π
Hooks
React hooks Hook the React feature
concept
Hooks provide a more direct, simple API for React.
hook
Like a much lower-level concept than a class component
StateDiagram -v2 Hook --> Class component React base --> hook
What problem was solved?
- Functional components cannot use React features like State, lifecycle hooks, ref, etc
- Class-based components have a threshold for beginners, while functional components are friendlier and class-based components can still be used. There is no distinction between the two
Pay attention to
-
State can be structured from the array returned by useState, setState()
-
SetState ([object | (preState) β {}])
useState
Function components cannot get react instances, so they cannot get state and setState().
import React, { Component, useState } from 'react'
// Function - If you start with use, React will assume you used a custom hook
function StateHook() {
const [count, setCount] = useState(0) / / π
return (
<div>
<h4>Home Component</h4>
<h5>count: {count}</h5>
<button onClick={()= > setCount(count + 1)}>count add</button>{/* pass function */}<button onClick={()= > setCount(count => count + 1)}>count add</button>
</div>)}Copy the code
- Contrast (Class component)
// Type ()
class StateHook extends Component{
state = {
count: 0
}
render() {
return (
<div>
<h4>Home Component</h4>
<h5>count: {this.state.count}</h5>
<button onClick={()= > this.setState({count: this.state.count + 1})}>count add</button>
</div>)}}Copy the code
useEffect
Functional components cannot use lifecycle hooks, and useEffect is designed to solve this problem
import React, { Component, useState, useEffect } from 'react'
// Function - If you start with use, React will assume you used a custom hook
function EffectHook() {
const [count, setCount] = useState(0)
useEffect(() = > {
// componentDidMount + componentDidUpdate
console.log('Page initialization completed/data update completed')
// Return the function equivalent to componentWillUnmount
return () = > {
// Do some cleaning, such as clearing a timer clearInterval()
// Strictly speaking, this is similar to componentWillUnmount only in terms of cleaning side effects, but is not the same as uninstalling hooks}})function add() {
setCount(count + 1)}return (
<div>
<h4>Home Component</h4>
<h5>count: {count}</h5>
<button onClick={add}>count add</button>
</div>)}Copy the code
- contrast
// Type ()
class EffectHook extends Component{
state = {
count: 0
}
componentDidMount() {
console.log('Page initialization completed')}componentDidUpdate() {
console.log('Update data completed')}render() {
return (
<div>
<h4>Home Component</h4>
<h5>count: {this.state.count}</h5>
<button onClick={()= > this.setState({count: this.state.count + 1})}>count add</button>
</div>)}componentWillUnmount() {
console.log('Component about to unload') // Just to demonstrate, the effect function returns a slightly different function,}}Copy the code
- UseEffect is equivalent to the integration of three hooks
React.useEffect(() = > {
let timer =setInterval(() = > {
add() ComponentDidMount and componentDidUpdate
},1000) // It depends on the second parameter array []
return () = > {
clearInterval(timer) // This returns a function
}
},[])
Copy the code
Parameter 2
Used to listen for changes to the page
// Do not pass the second argument, and any changes in the page will be listened for
React.useEffect(() = > {
/ /...
})
// Pass an empty array. No changes in the page are listened for
React.useEffect(() = > {
/ /...}, [])// Pass a specific state name, and the state change will be listened for
React.useEffect(() = > {
/ /...
},[stateName])
Copy the code
useRef
function RefHook() {
const [count, setCount] = useState(0)
const countRef = useRef() / / π
const headerRef = useRef()
function showInfo () {
console.log('countRef', countRef)
console.log('headerRef', headerRef)
}
return (
<div>
<h4>Home Component</h4>
<h5 ref={headerRef}>header</h5>
<input type="text" ref={countRef} onChange={showInfo}/>
</div>)}Copy the code
- contrast
Comparison (class)class RefHook extends Component{
state = {
count: 0
}
showInfo = () = > {
console.log('countRef'.this.refs.countRef)
console.log('headerRef'.this.refs.headerRef)
}
render() {
return (
<div>
<h4>Home Component</h4>
<h5 ref='headerRef' >header</h5>
<input type="text" ref='countRef' onChange={this.showInfo}/>
</div>)}}Copy the code
Customize the Hook
- Define our hook function starting with use
// This part of logic can be used in any other component
function useAllself(initState) {
const [count, setCount] = useState(initState)
useEffect(() = > {
console.log('count', count) / / to monitor
})
// If the value is even, return
return [count, setCount]
}
export default SelfHook
Copy the code
import React, { Component, useState, useEffect } from 'react'
function SelfHook() {
const [count, setCount] = useAllself(0) // Use our abstract hook
return (
<div>
<h4>Home Component</h4>
<h5>count: {count}</h5>
<button onClick={()= > setCount(count + 1)}>count add</button>
</div>)}Copy the code
Note: More about hookAPI
After the hook was added, the advantages of function components became extremely obvious. Now most of the development is also using function components, and I think this is also a trend in the future
expand
Some useful extensions
Fragment
JSX syntax requires that the outermost layer of each component must be wrapped with a tag, but this tag is redundant. In this case, using the Fragment component can solve the problem of redundant levels
use
import React, { Component,Fragment } from 'react'
export default class DemoFrag extends Component {
render() {
return (
<Fragment key={index}>
<p>111</p>
</Fragment>)}}Copy the code
- Both have the same effect, but empty tags don’t allow you to pass in any attributes, so if you need to pass in attributes, use fragments
export default class DemoFrag extends Component {
render() {
return (
<>
<p>111</p>
</>)}}Copy the code
Context
Context, which we talked about in the communications section, allows you to communicate across components between grandparent components and grandchild components
use
I suggest you take a look at the Demo code context communication
If you want to use the context in a different file, THEN I recommend that you create a separate file to expose the context
- To introduce the context
const MyContext = React.createContext()
const {
Provider,
Consumer
} = MyContext
Copy the code
- use
To make it more intuitive, assume that the following code is in the same file. If you want to see multiple files, see Demo
const MyContext = React.createContext()
const {Provider, Consumer} = MyContext
export default class Grandpa extends Component {
state = {
name: 'Link'.age: 18
}
render() {
const {name, age} = this.state
return (
<div className='grand'>
<h1>I am the ancestor component</h1>
<Provider value={{name,age}} >/ / π<Father />{/* Parent component */}</Provider>
</div>)}}Copy the code
- Sun component receiver (class)
Define a private contextType property based on MyContext
class Son extends Component {
static contextType = MyContext
render() {
const {name, age} = this.context
return (
<div className='son'>
<h1>This is Sun Component speaking</h1>
<p>I am :{name}, this year :{age}</p>
</div>)}}Copy the code
- Sun component receiver (functional)
Pass through the parameters received by the Consumer in the Context
function Son() {
return (
<div className='son'>
<h1>This is Sun Component speaking</h1>
<Consumer>
{
value => <p>I am :{value.name}, this year :{value.age}</p>
}
</Consumer>
</div>)}Copy the code
PureComponent
When data is changed, the render function is always executed regardless of whether setState changes the data, and if the component has other components nested within it, the child component’s render is also called, even if the data has not changed. This results in a lot of unnecessary re-rendering of components
The solution
- Override the shouldComponentUpdate hook
ShouldComponentUpdate by default returns true so that the rest of the component’s life cycle hooks can be called, if false is returned it is like a valve being turned off and the following hooks are not executed
Based on that, we can override it to determine if the props passed to the component have changed, and if so, we are letting it continue
shouldComponentUpdate(nextProps,nextState) {
// Block render if data does not change
console.log('Before change'.this.state, this.props);
console.log('After change', nextState, nextProps);
return !this.state.name === nextState
}
Copy the code
I won’t provide a Demo here, because I don’t usually do that, π€
- Make your component inherit PureComponent
import React, { Component, PureComponent } from 'react'
// Change the component's inheritance
class index extends PureComponent { / / π
/ /...
}
Copy the code
Note: ShouldComponentUpdate (shouldComponentUpdate) is overridden in PureComponent to better detect setState, props changes, but it only performs a shallow comparison of State. The memory address of SetState([object]) has changed, that is, the value of SetState([object]) without changing the address of the State object will still result in the render call
RenderProps
Change from passing components directly to the form of callback functions
Conventional props pass components
render() {
return (
<div className='index' >
<h1 >index</h1>
<A B={<B />} / > / / π</div>)}Copy the code
- A Component
class A extends Component {
render() {
return (
<div className='A'>
<h1>AAA</h1>{this. Props. B} / / π</div>)}Copy the code
This is very convenient, but you can’t pass parameters
The Render attribute passes a function
Pass A function to component A that accepts name, age
render() {
return (
<div className='index' >
<h1 >index</h1>
<A render={(name, age) = > <B name={name} age={age}/>} / > / / π</div>)}Copy the code
- A Component
Pass the data to component B as props
class A extends Component {
state = {
name: 'I'm the data that A passes to B'.age: 'Me too.'
}
render() {
const { name, age } = this.state
return (
<div className='A'>
<h1>AAA</h1>
{this.props.render( name, age )} // π
</div>)}}Copy the code
- B Component
class B extends Component {
render() {
return (
<div className='B'>
<h1>BBB</h1>
{this.props.name}<br />/ / π {this. Props. Age}</div>)}}Copy the code
ErrorBoundary
React If an error occurs in a subcomponent, the entire page cannot be rendered. In this case, error boundary handling is very important. However, it can only capture errors that occur during the life cycle of a subcomponent
use
Core hook: getDerivedStateFromError This hook returns an error object that catches whether a child component has an error
// Get a state from Error,
static getDerivedStateFromError(err) {
console.log('err', err)
return {hasErr: err}
}
render() {
return (
<div className='index' >
<h1 >index</h1>DOM*/} {this.state.haserr?<h1>There is an error here</h1> : <A />}
</div>)}Copy the code
- Record the error
componentDidCatch(error, errorInfo) {
console.log('There was an error.', the error, errorInfo)// After the error occurs, the operation can be recorded
}
Copy the code
- Note:
Error bounds cannot catch errors in the following scenarios:
- Event Handling (learn more)
- Asynchronous code (e.g
setTimeout
Β ζΒrequestAnimationFrame
Callback function) - Server side rendering
- Errors thrown by itself (not by its children)
(after)
The resources
1. controlled-vs-uncontrolledThe difference between controlled and uncontrolled components
2. ReactReact
3. React-routerThe React – the Router documentation
Thank π
This article is purely to teach myself how to learn React from my personal perspective. You may feel that I have mentioned many things in a short time, or that I have not explained them correctly. I also hope you can mention them. This is also where I may not be able to learn well. Thank you very much. You can see here
If you find the content helpful:
- β€οΈ welcome to focus on praise oh! I will do my best to produce high-quality articles
Contact author: Linkcyd π