笔记 | 尚硅谷React教程(衔接上篇)
第八章: React扩展
setState
示例代码:
import React, { Component } from 'react'
export default class Demo extends Component {
state = { count: 0 }
add = (params) => {
//1.对象式的srtState
/* //1.获取原来的count值
const { count } = this.state
//2.更新状态
this.setState({ count: count + 1 }, (params) => {
console.log("this.state.count", this.state.count);
})
// console.log("this.state.count", this.state.count); //0 */
//2.函数式的setState
this.setState(state => ({ count: state.count + 1 }))
}
render() {
return (
<div>
<h1>当前求和为: {this.state.count}</h1>
<button onClick={this.add}>点我加一</button>
</div>
)
}
}
lazyLoad
示例代码:
import React, { Component, lazy, Suspense } from 'react'
import { NavLink, Route, Routes } from 'react-router-dom'
import Loading from './Loading'
// import Home from './Home'
// import About from './About'
const Home = lazy(() => import('./Home'))
const About = lazy(() => import('./About'))
export default class Test extends Component {
render() {
return (
<div>
<div className='row'>
<div className='col-xs-offset-2 col-xs-8'>
<div className='page-header'><h2>React Router Demo</h2></div>
</div>
</div>
<div className='row'>
<div className='col-xs-2 col-xs-offset-2'>
<div className='list-group'>
<NavLink className='list-group-item' to='/about'>About</NavLink>
<NavLink className='list-group-item' to='/home'>Home</NavLink>
</div>
</div>
<div className='col-xs-6'>
<div className='panel'>
<div className='panel-body'>
<Suspense fallback={<Loading />}>
<Routes>
<Route path='/about' element={<About />} />
<Route path='/home' element={<Home />} />
</Routes>
</Suspense>
</div>
</div>
</div>
</div>
</div>
)
}
}
Hooks
StateHook
示例代码:
import React, { useState } from 'react'
//类式组件
/* class Demo extends Component {
state = { count: 0 }
add = () => {
this.setState((state) => ({ count: state.count + 1 }))
}
render() {
return (
<div>
<h2>当前求和为{this.state.count}</h2>
<button onClick={this.add}>点我加一</button>
</div>
)
}
} */
//函数式组件
function Demo() {
console.log("好")
const [count, setCount] = useState(0)
const [name, setName] = useState('tom')
//加的回调
function add() {
// setCount(count + 1)//第一种写法
setCount(count => count + 1)
}
function changeName() {
setName('Jack')
}
return (
<div>
<h2>当前求和为{count}</h2>
<h2>My Name is {name}</h2>
<button onClick={add}>点我加一</button>
<button onClick={changeName}>点我修改名字</button>
</div>
)
}
export default Demo
EffectHook
示例代码:
import React, { Component, useEffect, useState } from 'react'
import ReactDOM from 'react-dom/client';
import { root } from '../../index'
//类式组件
/* class Demo extends Component {
state = { count: 0 }
add = () => {
this.setState((state) => ({ count: state.count + 1 }))
}
unmount = () => {
// const root = document.getElementById('root')
root.unmount()
// ReactDOM.unmountComponentAtNode(document.getElementById('root'))
}
componentDidMount = () => {
this.timer = setInterval(() => {
this.setState(state => ({ count: state.count + 1 }))
}, 1000);
}
componentWillUnmount() {
clearInterval(this.timer);
}
render() {
return (
<div>
<h2>当前求和为{this.state.count}</h2>
<button onClick={this.add}>点我加一</button>
<button onClick={this.unmount}>卸载组件</button>
</div>
)
}
}
*/
//函数式组件
function Demo() {
const [count, setCount] = useState(0)
useEffect(() => {
let timer = setInterval(() => {
setCount(count => count + 1)
}, 1000);
return () => {
clearInterval(timer)
}
}, [])
//加的回调
function add() {
// setCount(count + 1)//第一种写法
setCount(count => count + 1)
}
//卸载组件的回调
function unmount() {
root.unmount()
}
return (
<div>
<h2>当前求和为{count}</h2>
<button onClick={add}>点我加一</button>
<button onClick={unmount}>卸载组件</button>
</div>
)
}
export default Demo
【注:React 18以及之后的版本StrictMode默认启用,并且在开发环境中会两次调用组件的副作用代码,也就是useEffect中】
RefHook
示例代码:
import React, { Component, createRef, useEffect, useRef, useState } from 'react'
import ReactDOM from 'react-dom/client';
import { root } from '../../index'
//类式组件
// class Demo extends Component {
// state = { count: 0 }
// myRef = createRef()
// add = () => {
// this.setState((state) => ({ count: state.count + 1 }))
// }
// unmount = () => {
// // const root = document.getElementById('root')
// root.unmount()
// // ReactDOM.unmountComponentAtNode(document.getElementById('root'))
// }
// show = () => {
// alert(this.myRef.current.value)
// }
// componentDidMount = () => {
// this.timer = setInterval(() => {
// this.setState(state => ({ count: state.count + 1 }))
// }, 1000);
// }
// componentWillUnmount() {
// clearInterval(this.timer);
// }
// render() {
// return (
// <div>
// <input type="text" ref={this.myRef} />
// <h2>当前求和为{this.state.count}</h2>
// <button onClick={this.add}>点我加一</button>
// <button onClick={this.unmount}>卸载组件</button>
// <button onClick={this.show}>点击提示数据</button>
// </div>
// )
// }
// }
//函数式组件
function Demo() {
const [count, setCount] = useState(0)
const myRef = useRef()
useEffect(() => {
let timer = setInterval(() => {
setCount(count => count + 1)
}, 1000);
return () => {
clearInterval(timer)
}
}, [])
//加的回调
function add() {
// setCount(count + 1)//第一种写法
setCount(count => count + 1)
}
//卸载组件的回调
function unmount() {
root.unmount()
}
//提示输入的回调
function show() {
alert(myRef.current.value)
}
return (
<div>
<input type="text" ref={myRef} />
<h2>当前求和为{count}</h2>
<button onClick={add}>点我加一</button>
<button onClick={unmount}>卸载组件</button>
<button onClick={show}>点我提示数据</button>
</div>
)
}
export default Demo
Fragment
示例代码:
import React, { Component, Fragment } from 'react'
export default class Demo extends Component {
render() {
return (
<Fragment key={1}>
<input type="text" />
<input type="text" />
</Fragment>
)
}
}
Context
示例代码:
import React, { Component, createContext } from 'react'
import './index.css'
//创建Context对象
const MyContext = createContext()
const { Provider, Consumer } = MyContext
export default class A extends Component {
state = { username: 'Tom', age: 19 }
render() {
const { username, age } = this.state
return (
<div className='parent'>
<h3>我是A组件</h3>
<h4>我的用户名是:{username}</h4>
<Provider value={{ username, age }}>
<B />
</Provider>
</div>
)
}
}
class B extends Component {
render() {
return (
<div className='child'>
<h3>我是B组件</h3>
<h4>我从A组件接收到的用户名是:{ }</h4>
<C />
</div>
)
}
}
/* class C extends Component {
//声明接收context
static contextType = MyContext
render() {
const { username, age } = this.context
console.log(this.context.username);
return (
<div className='grand'>
<h3>我是C组件</h3>
<h4>我从A组件接收到的用户名是:{username},年龄是{age}</h4>
</div>
)
}
} */
function C() {
return (
<div className='grand'>
<h3>我是C组件</h3>
<h4>我从A组件接收到的用户名是:
<Consumer>
{value => `${value.username},年龄是${value.age}`}
</Consumer>
</h4>
</div>
)
}
组件优化
示例代码:
import React, { Component, PureComponent } from 'react'
import './index.css'
export default class Parent extends PureComponent {
state = { carName: "benchi", stus: ['小张', '小李', '小王'] }
changeCar = () => {
// this.setState({ carName: 'maibahe' })
const obj = this.state
obj.carName = 'maibahe'
this.setState(obj)
}
addStu = () => {
/* const { stus } = this.state
stus.unshift('小刘')
this.setState({ stus }) */
const { stus } = this.state
this.setState({ stus: ['小刘', ...stus] })
}
// shouldComponentUpdate(nextProps, nextState) {
// console.log(this.props, this.state);//目前的props和state
// console.log(nextProps, nextState); //接下来变化的目标props,目标state
// return !this.state.carName === nextState.carName
// }
render() {
console.log("parent--render");
const { carName } = this.state
return (
<div className='parent'>
<h3>我是Parent组件</h3>
{this.state.stus}
<span>我的车名字是:{carName}</span><br />
<button onClick={this.changeCar}>点我换车</button>
<button onClick={this.addStu}>添加一个学生</button>
<Child carName="aotuo" />
</div>
)
}
}
class Child extends PureComponent {
// shouldComponentUpdate(nextProps, nextState) {
// console.log(this.props, this.state);//目前的props和state
// console.log(nextProps, nextState); //接下来变化的目标props,目标state
// return !this.props.carName === nextProps.carName
// }
render() {
console.log("child--render");
return (
<div className='child'>
<h3>我是Child组件</h3>
{/* <span>我接到的车是:{this.props.carName}</span> */}
</div>
)
}
}
render props
示例代码:
import React, { Component } from 'react'
import './index.css'
import C from '../setState'
export default class Parent extends Component {
render() {
return (
<div className='parent'>
<h3>我是Parent组件</h3>
<A render={(name) => <C name={name} />} />
</div>
)
}
}
class A extends Component {
state = { name: 'tom' }
render() {
console.log(this.props);
const { name } = this.state
return (
<div className='a'>
<h3>我是A组件</h3>
{/* <B /> */}
{this.props.render(name)}
</div>
)
}
}
class B extends Component {
render() {
console.log("bbb");
return (
<div className='b'>
<h3>我是B组件,{this.props.name}</h3>
</div>
)
}
}
Error boundary
示例代码:
Parent.jsx:
import React, { Component } from 'react'
import Child from './Child'
export default class Parent extends Component {
state = {
hasError: '' //用于标识子组件是否产生错误
}
//当Paretn的子组件出现报错时候,会触发getDrivedStateFromError调用,并携带错误信息
static getDerivedStateFromError(error) {
console.log('@@', error);
return { hasError: error }
}
componentDidCatch() {
//捕获错误并进行错误反馈
console.log("统计错误,反馈给服务器,用于通知编码人员进行bug的解决");
}
render() {
return (
<div>
<h2>我是Parent组件</h2>
{this.state.hasError ? <h2>当前网络不稳定,稍后再试</h2> : <Child />}
</div>
)
}
}
Child.jsx:
import React, { Component } from 'react'
export default class Child extends Component {
state = {
// users: 'abc',
users: [
{ id: '001', name: 'tom', age: 18 },
{ id: '002', name: 'jack', age: 19 },
{ id: '003', name: 'peiqi', age: 20 },
]
}
render() {
return (
<div>
<h2>我是Child组件</h2>
{this.state.users.map(userObj => {
return <h4 key={userObj.id}>{userObj.name}---{userObj.age}</h4>
})}
</div>
)
}
}
组件通信方式总结
第九章: React Router 6
概述
Component
BrowserRouter
HashRouter
Routes 与 Route
Link
NavLink
Navigate
Outlet
示例代码:
src/App.jsx:
import React from 'react'
import { NavLink, useRoutes } from 'react-router-dom'
import routes from './routes'
export default function App() {
//根据路由表生成对应的路由规则
const element = useRoutes(routes)
return (
<div>
<div className='row'>
<div className='col-xs-offset-2 col-xs-8'>
<div className='page-header'><h2>React Router Demo</h2></div>
</div>
</div>
<div className='row'>
<div className='col-xs-2 col-xs-offset-2'>
<div className='list-group'>
{/* 路由链接 */}
<NavLink className="list-group-item" to='/about'>About</NavLink>
{/* 添加end,如果子级路由匹配了,则自己失去高亮 */}
<NavLink className="list-group-item" end to='/home'>Home</NavLink>
</div>
</div>
<div className='col-xs-6'>
<div className='panel'>
<div className='panel-body'>
{/* 注册路由 */}
{element}
</div>
</div>
</div>
</div>
</div>
)
}
src/pages/Home.jsx:
import React from 'react'
import { NavLink, Outlet } from 'react-router-dom'
export default function Home() {
return (
<div>
<h3>我是Home的内容</h3>
<div>
<ul className='nav nav-tabs'>
<li>
<NavLink className="list-group-item" to='news'>News</NavLink>
</li>
<li>
<NavLink className="list-group-item" to='message'>Message</NavLink>
</li>
</ul>
{/* 指定路由组件呈现的位置 */}
<Outlet />
</div>
</div>
)
}
【Message和News组件中的内容省略】
src/routes/index.js:
import About from "../pages/About"
import Home from "../pages/Home"
import Message from "../pages/Message"
import News from "../pages/News"
import { Navigate } from 'react-router-dom'
export default [
{
path: '/about',
element: <About />
},
{
path: '/home',
element: <Home />,
children: [
{
path: 'news',
element: <News />
},
{
path: 'message',
element: <Message />
}
]
},
{
path: '/',
element: <Navigate to="/about" />
}
]
Hooks
useRoutes
示例代码:
src/App.jsx:
import React from 'react'
import { NavLink, useRoutes } from 'react-router-dom'
import routes from './routes'
export default function App() {
//根据路由表生成对应的路由规则
const element = useRoutes(routes)
return (
<div>
<div className='row'>
<div className='col-xs-offset-2 col-xs-8'>
<div className='page-header'><h2>React Router Demo</h2></div>
</div>
</div>
<div className='row'>
<div className='col-xs-2 col-xs-offset-2'>
<div className='list-group'>
{/* 路由链接 */}
<NavLink className="list-group-item" to='/about'> About</NavLink>
<NavLink className="list-group-item" to='/home'>Home</NavLink>
</div>
</div>
<div className='col-xs-6'>
<div className='panel'>
<div className='panel-body'>
{/* 注册路由 */}
{element}
</div>
</div>
</div>
</div>
</div>
)
}
src/routes/index.js:
import About from "../pages/About"
import Home from "../pages/Home"
import { Navigate } from 'react-router-dom'
export default [
{
path: '/about',
element: <About />
},
{
path: '/home',
element: <Home />
},
{
path: '/',
element: <Navigate to="/about" />
}
]
useNavigate
示例代码:【其他组件内容省略】
src/App.jsx:
import React from 'react'
import { NavLink, useRoutes } from 'react-router-dom'
import routes from './routes'
import Header from './components/Header'
export default function App() {
//根据路由表生成对应的路由规则
const element = useRoutes(routes)
return (
<div>
<div className='row'>
<Header />
</div>
<div className='row'>
<div className='col-xs-2 col-xs-offset-2'>
<div className='list-group'>
{/* 路由链接 */}
<NavLink className="list-group-item" to='/about'>About</NavLink>
{/* 添加end,如果子级路由匹配了,则自己失去高亮 */}
<NavLink className="list-group-item" end to='/home'>Home</NavLink>
</div>
</div>
<div className='col-xs-6'>
<div className='panel'>
<div className='panel-body'>
{/* 注册路由 */}
{element}
</div>
</div>
</div>
</div>
</div>
)
}
src/components/Header.jsx:
import React from 'react'
import { useNavigate } from 'react-router-dom'
export default function Header() {
const navigate = useNavigate()
function back() {
navigate(-1)
}
function forward() {
navigate(1)
}
return (
<div className='col-xs-offset-2 col-xs-8'>
<div className='page-header'>
<h2>React Router Demo</h2>
<button onClick={back}>⬅️后退</button>
<button onClick={forward}>前进➡️</button>
</div>
</div>
)
}
src/pages/Message.jsx:
import React, { useState } from 'react'
import { Link, Outlet, useNavigate } from 'react-router-dom'
export default function Message() {
const navigate = useNavigate()
const [messages] = useState(
[
{ id: '001', title: '消息1', content: '锄禾日当午' },
{ id: '002', title: '消息2', content: '汗滴禾下土' },
{ id: '003', title: '消息3', content: '谁知盘中餐' },
{ id: '004', title: '消息4', content: '粒粒皆辛苦' },
]
)
function showDetail(m) {
navigate('detail', {
replace: true,
state: {
id: m.id,
title: m.title,
content: m.content
}
})
}
return (
<div>
<ul>
{messages.map(m => {
return (
//路由链接
<li key={m.id}>
<Link
to="detail"
state={{
id: m.id,
title: m.title,
content: m.content
}}>{m.title}</Link>
<button onClick={() => showDetail(m)}>查看详情</button>
</li>
)
})}
</ul>
<hr />
{/* 指定路由组件的展示位置 */}
<Outlet />
</div>
)
}
useParams(路由的params参数)
示例代码:【其他组件内容省略】
src/routes/index.js:
import About from "../pages/About"
import Home from "../pages/Home"
import Message from "../pages/Message"
import News from "../pages/News"
import Detail from "../pages/Detail"
import { Navigate } from 'react-router-dom'
export default [
{
path: '/about',
element: <About />
},
{
path: '/home',
element: <Home />,
children: [
{
path: 'news',
element: <News />
},
{
path: 'message',
element: <Message />,
children: [
{
path: 'detail/:id/:title/:content',
element: <Detail />
}
]
}
]
},
{
path: '/',
element: <Navigate to="/about" />
}
]
src/pages/Message.jsx:
import React, { useState } from 'react'
import { Link, Outlet } from 'react-router-dom'
export default function Message() {
const [messages] = useState(
[
{ id: '001', title: '消息1', content: '锄禾日当午' },
{ id: '002', title: '消息2', content: '汗滴禾下土' },
{ id: '003', title: '消息3', content: '谁知盘中餐' },
{ id: '004', title: '消息4', content: '粒粒皆辛苦' },
]
)
return (
<div>
<ul>
{messages.map(m => {
return (
//路由链接
<li key={m.id}>
<Link to={`detail/${m.id}/${m.title}/${m.content}`}>{m.title}</Link>
</li>
)
})}
</ul>
<hr />
{/* 指定路由组件的展示位置 */}
<Outlet />
</div>
)
}
src/pages/Detail.jsx:
import React from 'react'
import { useParams, useMatch } from 'react-router-dom'
export default function Detail() {
const { id, title, content } = useParams()
// const x = useMatch('/home/message/detail/:id/:title/:content')
// console.log("x", x);
return (
<ul>
<li>消息编号:{id}</li>
<li>消息标题:{title}</li>
<li>消息内容:{content}</li>
</ul>
)
}
useSearchParams(路由的search参数)
示例代码:【其他组件内容省略】
src/routes/index.js:
import About from "../pages/About"
import Home from "../pages/Home"
import Message from "../pages/Message"
import News from "../pages/News"
import Detail from "../pages/Detail"
import { Navigate } from 'react-router-dom'
export default [
{
path: '/about',
element: <About />
},
{
path: '/home',
element: <Home />,
children: [
{
path: 'news',
element: <News />
},
{
path: 'message',
element: <Message />,
children: [
{
path: 'detail',
element: <Detail />
}
]
}
]
},
{
path: '/',
element: <Navigate to="/about" />
}
]
src/pages/Message.jsx:
import React, { useState } from 'react'
import { Link, Outlet } from 'react-router-dom'
export default function Message() {
const [messages] = useState(
[
{ id: '001', title: '消息1', content: '锄禾日当午' },
{ id: '002', title: '消息2', content: '汗滴禾下土' },
{ id: '003', title: '消息3', content: '谁知盘中餐' },
{ id: '004', title: '消息4', content: '粒粒皆辛苦' },
]
)
return (
<div>
<ul>
{messages.map(m => {
return (
//路由链接
<li key={m.id}>
<Link to={`detail?id=${m.id}&title=${m.title}&content=${m.content}`}>{m.title}</Link>
</li>
)
})}
</ul>
<hr />
{/* 指定路由组件的展示位置 */}
<Outlet />
</div>
)
}
src/pages/Detail.jsx:
import React from 'react'
import { useSearchParams, useLocation } from 'react-router-dom'
export default function Detail() {
const [search, setSearch] = useSearchParams()
const id = search.get('id')
const title = search.get('title')
const content = search.get('content')
const x = useLocation()
console.log("x", x);
return (
<ul>
<li><button onClick={() => setSearch('id=008&title=哈哈&content=嘻嘻')}>点我更新一下收到的search参数</button></li>
<li>消息编号:{id}</li>
<li>消息标题:{title}</li>
<li>消息内容:{content}</li>
</ul >
)
}
useLocation(路由的search参数、state参数)
路由的state参数示例代码:【其他组件内容省略】
src/pages/Message.jsx:
import React, { useState } from 'react'
import { Link, Outlet } from 'react-router-dom'
export default function Message() {
const [messages] = useState(
[
{ id: '001', title: '消息1', content: '锄禾日当午' },
{ id: '002', title: '消息2', content: '汗滴禾下土' },
{ id: '003', title: '消息3', content: '谁知盘中餐' },
{ id: '004', title: '消息4', content: '粒粒皆辛苦' },
]
)
return (
<div>
<ul>
{messages.map(m => {
return (
//路由链接
<li key={m.id}>
<Link
to="detail"
state={{
id: m.id,
title: m.title,
content: m.content
}}>{m.title}</Link>
</li>
)
})}
</ul>
<hr />
{/* 指定路由组件的展示位置 */}
<Outlet />
</div>
)
}
src/pages/Detail.jsx:
import React from 'react'
import { useLocation } from 'react-router-dom'
export default function Detail() {
const { state: { id, title, content } } = useLocation()
return (
<ul>
<li>消息编号:{id}</li>
<li>消息标题:{title}</li>
<li>消息内容:{content}</li>
</ul >
)
}
useMatch(路由的params参数)
useInRouterContext
示例代码:【其他组件内容省略】:
src/index.js:
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from 'react-router-dom'
import App from "./App";
import Demo from "./pages/Demo";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
<Demo />
</React.StrictMode>
);
src/pages/Demo.jsx:
import React from 'react'
import { useInRouterContext } from 'react-router-dom';
export default function Demo() {
console.log(useInRouterContext()); //false
return (
<div>
<h2>我是Demo的内容</h2>
</div>
)
}
useNavigationType
useOutlet
useResolvedPath
示例代码:
console.log(useResolvedPath('/user?id=001&name=tom#qwe'));
/*
{
"pathname": "/user",
"search": "?id=001&name=tom",
"hash": "#qwe"
} */