目录
组件间的通信方式
传递接收params,search,state参数,goBack(),goForward()
创建脚手架命令:
create-react-app 项目名
组件间的通信方式
组件通信方式:
1.父子间通信:
父组件:
<child abc={xxx}/>
子组件:
props.abc拿到数据
2.兄弟间通信:(1)消息订阅与发布,redux集中式管理
(2)找到共同的父亲,然后将数据放在父亲身上,再通过子传父和父传子的方式来获取数据
3.(1)消息订阅与发布,redux集中式管理
(2)祖孙通信Context:const {Provider,Consumer} = React.createContext()
祖先组件:
<Provider value={xxx}>
</Provider>
孙子组件:
<Consumer>
(value)=>{
return()}
</Consumer>
props校验:
安装prop-type包,然后引入在要用的组件中import PropTypes from 'prop-type'
例如:App组件中的Porps校验
App.propTypes={
colors:PropTypes.array
fn:PropTypes.func.isRequired//表示传过来的是一个函数,并且是必传项
}//添加props默认值
App.defaultProps = {
pageSize: 10//当其他组件没有传pageSize值给App时,默认接收的是10
}
Router6:useNavigationType, useResolvedPath
import React from 'react'
import { useNavigationType, useResolvedPath } from 'react-router-dom'
export default function Demo() {
/* 作用:返回当前的导航类型(用户是如何来到当前页面的)
返回值:POP、PUSH、RESPLACE
备注:POP是指在浏览器中直接打开了这个路由组件(刷新页面) */
console.log(useNavigationType())
// 作用:给定一个URL值,解析其中的:path、search、hash值
console.log(useResolvedPath('/user?id=001&name=tom#qwe'))
// {pathname: '/user', search: '?id=001&name=tom', hash: '#qwe'}
return (
<div>Demo</div>
)
}
Router6:<Outlet/>
<Outlet/>:当<Route>与useRoutes()一起使用配置‘路由表‘,则需要<Outlet>来渲染子路由
Router6:params,search,state路由传参,编程式路由导航navigate,navigate前进与后退
//在routes.js组件中:
import { lazy } from "react";
import { Navigate } from "react-router-dom";
// const Home = lazy(()=>import('./pages/Home'))
// const About = lazy(()=>import('./pages/About'))
import Home from './pages/Home'
import About from './pages/About'
import Message from './pages/Message'
import News from './pages/News'
import Msgdetail from './pages/Msgdetail'
import Newsdetail from './pages/Newsdetail'
export default [
// 路由重定向
{
path:'/',
element:<Navigate to='/about'/>
},
{
path:'/about',
element:<About/>
},
{
path:'/home',
element:<Home/>,
children:[
{
path:'message',
element:<Message/>,
children:[
{
path:'detail',
element:<Msgdetail/>
}
]
},
{
path:'news',
element:<News/>,
children:[
{
path:'detail',
element:<Newsdetail/>
}
]
}
]
}
]
//在MyNavLink组件中:
import React, { Component } from 'react'
import { NavLink } from 'react-router-dom'
export default class MyNavLink extends Component {
render() {
// console.log(this.props)
return (
<div>
<NavLink {...this.props}></NavLink>
</div>
)
}
}
//App.js组件中:
import react from 'react'
import { Link ,Route ,NavLink,useRoutes} from 'react-router-dom'
import './pages/App.css'
import MyNavLink from './components/MyNavLink'
import Header from './components/Header'
import routes from './routes'
function App(){
const element = useRoutes(routes)
return (
<div>
<Header/>
<hr/>
<div className='route_show'>
<ul >
{/* <Link to='/home'>home</Link>
<Link to='/about'>about</Link> */}
{/* <NavLink to='/home'>home</NavLink>
<NavLink to='/about'>about</NavLink> */}
{/* 对NavLink进行二次封装 */}
<MyNavLink to="/about">about</MyNavLink>
<MyNavLink to="/home">home</MyNavLink>
</ul>
<div className='content_right'>
{element}
</div>
</div>
</div>
)
}
export default App
//在Home组件中:
import React, { Component } from 'react'
import MyNavLink from '../components/MyNavLink'
import { Route ,Routes,Outlet} from 'react-router-dom'
// import Message from './Message'
// import News from './News'
export default class Home extends Component {
render() {
return (
<div>
<li>我是Home组件</li>
<ol>
<li>
<MyNavLink to="news">News</MyNavLink>
<MyNavLink to="message">Message</MyNavLink>
</li>
<hr />
{/* <Switch className="route">
<Route path="/home/news" component={News}></Route>
<Route path="/home/message" component={Message}></Route>
</Switch> */}
{/* <Routes className="route">
<Route path="/home/news" element={<News/>}></Route>
<Route path="/home/message" element={<Message/>}></Route>
</Routes> */}
<Outlet/>
</ol>
</div>
)
}
}
//在Message组件中:
import React, { Component, useState } from 'react'
import { Route, Link, Outlet, useNavigate } from 'react-router-dom'
export default function Message() {
const navigate = useNavigate()
const [state, setState] = useState([
{ id: 1, title: 'msg1', content: 'love china' },
{ id: 2, title: 'msg2', content: 'love jx' },
{ id: 3, title: 'msg3', content: 'love sr' },
])
function showMsgdetail(v) {
navigate('detail', {
// 编程式路由导航只能传递state参数,params和search参数是直接写在要跳转的链接或者路由后面进行拼接
replace: false,
state: {
id: v.id,
title: v.title
}
})
}
return (
<div>
<ul>
{
state.map(v => {
return (
<li key={v.id}>
{/* 传递prarms参数 */}
{/* <Link to={`/home/message/detail/${v.id}/${v.title}`}>{v.title}</Link> */}
{/* 传递search(query)参数 */}
{/* <Link to={`/home/message/detail?id=${v.id}&title=${v.title}`}>{v.title}</Link> */}
{/* 传递state参数 */}
{/* <Link to='detail' state={{id:v.id,title:v.title}}>{v.title}</Link> */}
<button onClick={() => showMsgdetail(v)}>点击传递state参数跳转到msgdetail页面(编程式路由push)</button>
</li>
)
})
}
<Outlet />
</ul>
</div>
)
}
//在MsgDetail组件中:
import React, { Component } from 'react'
import { useLocation, useParams ,useSearchParams} from 'react-router-dom'
const data = [
{id:1,content:'love china'},
{id:2,content:'love jx'},
{id:3,content:'love sr'},
]
export default function Msgdetail(){
// 接收params参数
/* const param = useParams()
const {id,title} = param */
// 接收search参数,修改search参数的方法
/* const [search,setSearch] = useSearchParams()
const id = search.get('id')
const title = search.get('title') */
// 接收state参数
const {state:{id,title}} = useLocation()
let resultContent = data.find(v=>{
return v.id==id
})||{}
return (
<p>
<li>{id}</li>
<li>{title}</li>
<li>{resultContent.content}</li>
{/* setSearch:改变search参数的方法 */}
{/* <button onClick={()=>setSearch("id='7'&title='hhh'")}>点击改变search参数</button> */}
</p>
)
}
//在Header非路由组件中实现前进与后退:
import React, { Component } from 'react'
import { useNavigate } from 'react-router-dom'
function Header() {
const navigate = useNavigate()
function goBack(){
// router5
// this.props.history.goBack()
// router6
navigate(-1)
}
function goForward(){
// this.props.history.goForward()
navigate(1)
}
return (
<div className='react_header'>
<h2>React-router-dom</h2>
<button onClick={goBack}>回退</button>
<button onClick={goForward}>前进</button>
</div>
)
}
export default Header
Router6hook:useResolvedPath()
//作用:给定一个URL值,解析其中的path、search、hash值
Router6:Navigate,element,Routes,NavLink自定义高亮显示
//在App.js组件中:
import React, { Component } from 'react'
import { NavLink, Routes, Route, Navigate ,useRoutes} from 'react-router-dom'
import Fun from './Navigate'
import Error from './Error'
function App(){
function computeClassName({isActive}){
return isActive ? 'linkFont activeLink' : 'linkFont'
}
return (
<div>
//className:接收一个函数
<NavLink className={computeClassName} to='/navigate'>navigate</NavLink>
<NavLink className={computeClassName} to='/error'>error</NavLink>
<div>
<Routes>
<Route path='/navigate' element={<Fun />} />
<Route path='/error' element={<Error />} />
<Route path='/' element={<Navigate to='/error' />} />
</Routes>
</div>
</div>
)
}
export default App
//在Navigate组件中:
import React, { useState } from 'react'
import { Navigate } from 'react-router-dom'
export default function Nav() {
const [sum,setSum] = useState(1)
return (
<div>
{/* 重定向Navigate:指定跳转到指定页面 */}
{sum==2?<Navigate to='/error'/>:<h2>sum:{sum}</h2>}
<button onClick={()=>setSum(2)}>点击sum变为2</button>
</div>
)
}
Router6:路由表
//在src下新建routes.js:
import { lazy } from "react";
import { Navigate } from "react-router-dom";
// const Nav = lazy(()=>import('./Navigate'))
// const Error = lazy(()=>import('./Error'))
import Nav from "./Navigate";
import Error from './Error'
export default [
{
path:'/navigate',
element:<Nav/>
},
{
path:'/error',
element:<Error/>
},
{
path:'/',
element:<Navigate to='/navigate'/>
}
]
//在App.js组件中:
import React, { Component } from 'react'
import { NavLink,useRoutes} from 'react-router-dom'
import routes from './routes'
function App(){
const element = useRoutes(routes)
return (
<div>
<NavLink to='/navigate'>navigate</NavLink>
<NavLink to='/error'>error</NavLink>
<div>
{element}
</div>
</div>
)
}
export default App
useCallback
// 防止因为组件重新渲染,导致方法被重新创建,起到缓存作用,只有第二个参数变化,才重新声明渲染
const Fun = useCallback(()=>{
// 函数
()=>{}
},[name])
useMemo
const a = useMemo(()=>{
// 函数
()=>{}
},[name])
//与Vue中的compute属性类似,a得到的结果是useMemo第一个函数参数的返回值,第二个参数是监测改变的数据
//如果数组中的name没有发生变化,则函数会采取缓存的方式,return回上次的返回值
配置代理
在src/setupProxy.js中:
const proxy = require('http-proxy-middleware')
module.exports = function(app){
app.use(
proxy('/api',{
target:'http://localhost:5000',
changeOrigin:true,
pathRewrite:{'^/api':""}
})
)
}
React-router-dom
Link,NavLink
//在src/index.js中
import {BrowserRouter} from 'react-router-dom'
//import {HashRouter} from 'react-router-dom':可以解决刷新页面后样式丢失问题#后面默认为是前端资源,不带给服务器
ReactDOM.render(
<BrowserRouter>
<App/>
</BrowserRouter>,
document.getElementById('root')
)
//在组件中
import {Link ,Route} from 'react-router-dom'
//路由链接
//<Link className="item" to="/about">路由跳转</Link>
//NavLink中有一个属性activeName:点击跳转到哪个路由哪个标签高亮
<NavLink activeName="abc" className="item" to="/about">路由跳转到about</Link>
<NavLink activeName="abc" className="item" to="/home">路由跳转home</Link>
//注册路由
<Route path="/about" component={About}/>
NavLink的二次封装
//在App.js组件中:
import react from 'react'
import { Route } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import MyNavLink from './components/MyNavLink'
class App extends react.Component {
render() {
return (
<div className='route_show'>
<ul >
{/* <NavLink to='/home'>home</NavLink>
<NavLink to='/about'>about</NavLink> */}
{/* 对NavLink进行二次封装 */}
<MyNavLink to="/about">about</MyNavLink>
<MyNavLink to="/home">home</MyNavLink>
//在MyNavLink中通过this.props.children可以获取该标签体中的内容,而无需与vue一样使用插槽
</ul>
<div className='content_right'>
//exact:开启精准匹配
<Route path='/about' exact component={About}></Route>
<Route path='/home' component={Home}></Route>
</div>
</div>
)
}
}
export default App
//在MynavLink组件中:
import React, { Component } from 'react'
import { NavLink } from 'react-router-dom'
export default class MyNavLink extends Component {
render() {
console.log(this.props)
return (
<div>
<NavLink activeClassName='active' {...this.props}></NavLink>
</div>
)
}
}
Router5:
Switch,Redirect
//在router5中使用Switch和component配合:用于提高代效率,使得path与对应component匹配到之后就不再往下继续匹配
//在router6中使用Routes和element配合
import { Route ,Switch ,Routes ,Redirect ,Navigate} from 'react-router-dom'
/*<Switch>
<Route path='/about' component={About}></Route>
<Route path='/home' component={Home}></Route>
//router5中重定向路由,默认页面一开始就展示about页面
<Redirect to="about"/>
</Switch>*/
<Routes className="route">
//caseSensitive :指定path匹配时是否区分大小,写默认为false
<Route caseSensitive path="/home/news" element={<News/>}></Route>
<Route path="/home/message" element={<Message/>}></Route>
//router6重定向方式:
<Route path='/' element={<Navigate to='/about'/>></Route>
</Routes>
传递接收params,search,state参数,goBack(),goForward()
//在message组件中:
// 传递params参数
// 传递params参数
import React, { Component } from 'react'
import { Route ,Link} from 'react-router-dom'
import Msgdetail from './Msgdetail'
export default class Message extends Component {
state = {
messageList:[
{id:1,title:'msg1',content:'love china'},
{id:2,title:'msg2',content:'love jx'},
{id:3,title:'msg3',content:'love sr'},
]
}
//用push进行路由跳转
pushShow = (id,title)=>{
// 接收state参数进行路由跳转
this.props.history.push('/home/message/detail',{id:id,title:title})
//this.props.history.goBack()//回退
}
replaceShow = (id,title)=>{
// 接收state参数进行路由跳转
this.props.history.replace('/home/message/detail',{id:id,title:title})
//this.props.history.goForward()//前进
}
render() {
return (
<div>
<ul>
{
this.state.messageList.map(v=>{
return (
<li key={v.id}>
{/* 传递prarms参数 */}
{/* <Link to={`/home/message/detail/${v.id}/${v.title}`}>{v.title}</Link> */}
{/* 传递search(query)参数 */}
{/* <Link to={`/home/message/detail?id=${v.id}&title=${v.title}`}>{v.title}</Link> */}
{/* 传递state参数 replace:消除历史痕迹*/}
<Link replace to={{pathname:'/home/message/detail',state:{id:v.id,title:v.title}}}>{v.title}</Link>
<button onClick={this.pushShow(v.id,v.title)}>编程式路由有历史缓存push</button>
<button onClick={this.replaceShow(v.id,v.title)}>编程式路由无历史缓存replace</button>
</li>
)
})
}
{/* 声明接受query参数 */}
{/* <Route path="/home/message/detail/:id/:title" component={Msgdetail}/> */}
{/* 接收search,state参数,都无需声明 */}
<Route path="/home/message/detail" component={Msgdetail}/>
</ul>
</div>
)
}
}
//在Msgdetail中:
import React, { Component } from 'react'
// import qs from 'querystring'
const data = [
{id:1,content:'love china'},
{id:2,content:'love jx'},
{id:3,content:'love sr'},
]
export default class Msgdetail extends Component {
render() {
// console.log(this.props)
//接收params参数
// const {id,title} = this.props.match.params
//接收search参数
// id=1&title=msg1:是urlencode的编码形式
/* const {search} =this.props.location
let searchquery = qs.parse(search.slice(1))
const {id,title} =searchquery */
// 接收state参数:导航栏中不显示传递的参数
const {id,title} =this.props.location.state||{}
console.log(this.props.location)
let resultContent = data.find(v=>{
return v.id==id
})||{}
return (
<p>
<li>{id}</li>
<li>{title}</li>
<li>{resultContent.content}</li>
</p>
)
}
}
withRouter
//在非路由组件中:
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
class Header extends Component {
goBack = ()=>{
this.props.history.goBack()
}
goForward = ()=>{
this.props.history.goForward()
}
render() {
return (
<div className='react_header'>
<h2>React-router-dom</h2>
<button onClick={this.goBack}>回退</button>
<button onClick={this.goForward}>前进</button>
</div>
)
}
}
//widthRouter:加工非路由组件,让非路由组件也具有路由组件中的Api(如:this.props中的值),widthRouter的返回值是一个新组件
export default withRouter(Header)
redux+react-redux:用于多个组件间状态共享
//在index.js入口文件中:
import React from 'react'//JSX语法需要
import {createRoot} from 'react-dom/client'//可以帮我们把React组件渲染到页面上去
import { Provider } from 'react-redux'
import App from './App'
import store from './redux/store'
const container = document.getElementById('root')
const root = createRoot(container)
root.render(
// 将store传给每一个容器组件
<Provider store={store}>
<App/>
</Provider>
)
// 获取到redux中的数据后即使更新组件(如果使用了react-redux第三方库,则无需subscribe监测组件状态的变化
/* store.subscribe(()=>{
root.render(
<BrowserRouter>
<App/>
</BrowserRouter>
)
}) */
//在App.js组件中:
import React, { Component } from 'react'
// 引入的是count组件的父容器
import Count from './containers/Count'
import Person from './containers/Person'
// 统一在index.js中使用react-redux第三方库提供的Provider组件来将store传给每一个容器组件
// import store from './redux/store'
export default class App extends Component {
render() {
return (
// <div><Count store={store}/></div>
<div>
<Count/>
<hr/>
<Person/>
</div>
)
}
}
//在redux/store.js中:
import { createStore ,applyMiddleware} from "redux";
// 使得异步action接收函数为返回值可以被store允许
import thunk from "redux-thunk";
import reducers from './reducers'
export default createStore(reducers,applyMiddleware(thunk))
//在redux/reducers/index.js中:
import { combineReducers} from "redux";
import countRedux from './count'
import personRedux from "./person";
export default combineReducers({count:countRedux,person:personRedux})
//在redux/reducers/count.js中:
const initState = 0
export default function (preState = initState, action) {
const { type, data } = action
switch (type) {
case 'increment':
return preState + data
case 'decrement':
return preState - data
default :
return preState
}
}
//在redux/reducers/person.js组件中:
import { ADD_PERSON } from "../constant"
const initState = [{id:'001',name:'tom',age:19}]
export default function(preState=initState,action){
const {type,data} = action
switch(type){
case ADD_PERSON:
return [data,...preState]
default:
return preState
}
}
//在redux/constant.js中:
// 定义常量:便于管理
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
export const ADD_PERSON ='add_person'
//在redux/actions/count.js中:
import { INCREMENT,DECREMENT } from "./constant";
export const createIncrementAction = data => ({type:INCREMENT,data})
// 如果action的返回值为一个对象,则为同步action
export const createDecrementAction = data => ({type:DECREMENT,data})
export const createIncrementAsyncAction = (data,time)=>{
// 由store调用,传入dispatch参数
// 如果action的返回值为一个函数,则为异步action
return (dispatch)=>{
setTimeout(()=>{
dispatch(createIncrementAction(data))
},time)
}
}
//在redux/actions/person.js中:
import {ADD_PERSON} from '../constant'
export const addPerson = personObj=>({type:ADD_PERSON,data:personObj})
//在constiner/Count.js组件中:
import { connect } from "react-redux";
import { createIncrementAction,createDecrementAction,createIncrementAsyncAction } from '../redux/count_actions'
import React, { Component, createRef } from 'react'
// import store from '../redux/store'
class Count extends Component {
state = {count:0}
componentDidMount(){
console.log(this.props,'count-------------')
}
increment = ()=>{
const {value} = this.myRef
this.props.createIncrementAction(value*1)
}
decrement = ()=>{
const {value} = this.myRef
this.props.createDecrementAction(value*1)
}
incrementOdd = ()=>{
const {value} = this.myRef
const count = this.props.count
if(count%2!==0){
// store.dispatch(createIncrementAction(value*1))
// 借用react-redux第三方库为中间人
this.props.createIncrementAsyncAction(value*1)
}
}
incrementAsync = ()=>{
const {value} = this.myRef
this.props.jiaAsync(value*1,500)
}
render() {
const {count,personNumber} =this.state
return (
<div>
<h1>Count的值为:{this.props.count},person组件有{this.props.personNumber}人</h1>
<select name="" id="" ref={v=>this.myRef=v}>
<option value={1}>1</option>
<option value={2}>2</option>
<option value={3}>3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementOdd}>奇数+</button>
<button onClick={this.incrementAsync}>异步+</button>
</div>
)
}
}
// 接收count组件的状态,返回值是一个对象
/* const mapStateToProps = (state)=>{
return {count:state}
} */
// 接收count组件的方法,返回值是一个对象
// mapActionToProps一般写法
/* const mapActionToProps = (dispatch)=>{
return {
jia:(number)=>{dispatch(createIncrementAction(number)) },
jian:(number)=>{dispatch(createDecrementAction(number))},
jiaAsync:(number,time)=>{
setTimeout(()=>{
dispatch(createIncrementAsyncAction(number))
},time)
}
}
} */
export default connect(
state=>({
count:state.count,
personNumber:state.person.length
}),
// mapActionToProps简化版
// 第二个参数可以接收一个function,也可以接收一个对象
{
createIncrementAction,//react-redux默认会帮忙调用dispatch
createDecrementAction,
createIncrementAsyncAction
}
)(Count)
//在contsiners/Person.js组件中:
import React, { Component } from 'react'
import {addPerson} from '../redux/actions/person'
import { connect } from 'react-redux'
class Person extends Component {
addPerson = ()=>{
let {value:name} = this.nameRef
let {value:age} = this.ageRef
// console.log(name,age)
let personObj = {id:Math.random()*99999,name,age}
this.props.addPerson(personObj)
// console.log(this.props)
this.nameRef.value=''
this.ageRef.value=''
}
render() {
const {person,count} = this.props
// console.log(this.props)
return (
<div>
<input type="text" placeholder='请输入名字' ref={v=>this.nameRef=v}/>
<input type="text" placeholder='请输入年龄' ref={v=>this.ageRef=v}/>
<button onClick={this.addPerson}>添加</button><br/>
<div>count组件求和为{count}</div>
<ul>
{
person.map(p=>{
return (<li key={p.id}><span>{p.name}</span>:<span>{p.age}</span></li>)
})
}
</ul>
</div>
)
}
}
export default connect(
state=>(
{person:state.person,
count:state.count}
),
{
addPerson
}
)(Person)
setState的函数写法与对象写法
import React, { Component } from 'react'
export default class Count extends Component {
state = {
count:0
}
add = ()=>{
// this.setState对象写法
/* this.setState({count:this.state.count+1},()=>{
console.log(this.state.count,'对象写法回调函数------')//1
}) */
// this.setState函数写法
this.setState((state,props)=>{
return ({count:state.count+1})
},()=>{
console.log(this.state.count,'函数写法回调函数------')
})
// console.log(this.state.count):0
}
// setState异步更新状态,接收两个参数({},()=>{}):()=>{}:在setState异步更新状态结束后,并且render调用后执行
render() {
return (
<div>
<div>Count的值为:{this.state.count}</div>
<button onClick={this.add}>点击+1</button>
</div>
)
}
}
lazy:实现路由懒加载、Suspense
import React,{Component,lazy,Suspense} from 'react'
import { Link ,Route ,Redirect} from 'react-router-dom'
import Header from './components/Header'
import './App.css'
// import About from './pages/About'
// import Home from './pages/Home'
// 路由懒加载:按需加载
const Home = lazy(()=>import ('./pages/Home'))
const About = lazy(()=>import ('./pages/About'))
class App extends Component {
render() {
return (
<div>
<Header/>
<hr/>
<div className='route_show'>
<ul >
<Link to='/home'>home</Link>
<Link to='/about'>about</Link>
</ul>
<div className='content_right'>
{/* Suspense:用于指定页面出错加载不出来时而展示给用户看的页面 ,fallback:接收一个组件*/}
<Suspense fallback={<h1>loading...</h1>}>
<Route path='/about' component={About}></Route>
<Route path='/home' component={Home}></Route>
<Redirect to="/about"/>
</Suspense>
</div>
</div>
</div>
)
}
}
export default App
函数式组件
useState、useEffect、useRef
import React, { Component, useRef } from 'react'
import { root } from './index'
/* export default class FunUse extends Component {
state = {count:0}
componentDidMount(){
this.timer = setInterval(()=>{
this.setState({count:this.state.count+1})
},500)
}
increment = ()=>{
this.setState(state=>({count:state.count+1}))
}
componentWillUnmount(){
clearInterval(this.timer)
}
unMount = ()=>{
// 卸载组件
root.unmount()
}
render() {
return (
<div>
<h2>Count的值为:{this.state.count}</h2>
<button onClick={this.increment}>点击+1</button>
<button onClick={this.unMount}>点击卸载组件</button>
</div>
)
}
} */
export default function FunUse(){
const [count,setCount] = React.useState(0)
const myRef = React.useRef()
/*
React.useEffect(()=>{},[]):
第二个参数为[]时=componentDidMount,不写第二个参数时=componentDidUpdated
第二个参数为[count]:表示只监测count状态的改变,只有count状态改变才会重新调用useEffect,
useEffect()的第一个参数接收一个函数作为返回值,该返回值的函数 = componentWillUnmount
*/
React.useEffect(()=>{
let timer = setInterval(()=>{
// 在useEffect中使用定时器时只能使用函数的更新方式
setCount(count=>count+1)
},1000)
return ()=>{
clearInterval(timer)
console.log('即将卸载组件')
}
},[])
const increment = ()=>{
// 第一种写法
setCount(count+1)
// setCount(value=>newValue):第二种写法
// setCount(count=>count+1)
}
const unMount = ()=>{
console.log('卸载组件')
root.unmount()
}
const getData = ()=>{
console.log(myRef.current.value)
}
return (
<div>
<input type="text" ref={myRef}/>
<button onClick={getData}>点击获取input框中的值(useRef)</button>
<h2>Count的值为:{count}</h2>
<button onClick={increment}>点击+1(useState)</button>
<button onClick={unMount}>点击卸载组件(useEffect)</button>
</div>
)
}
Fragment
import React, { Component ,Fragment} from 'react'
export default class Fragment extends Component {
render() {
return (
<Fragment>
{/* 作为根标签,不会渲染到页面上 */}
</Fragment>
)
}
}
Context
import React, { Component ,Fragment} from 'react'
// 创建Context容器对象
const MyContext = React.createContext()
const {Provider} = MyContext
export default class One extends Component {
state = {name:'jack'}
render() {
return (
<Fragment>
{/* 传入想要给后代的值value */}
<Provider value={this.state.name}>
<Two/>
</Provider>
</Fragment>
)
}
}
class Two extends Component {
render() {
return (
<div>
<Three/>
</div>
)
}
}
function Three(){
return (
<MyContext.Consumer>
{
// 用一个函数接收从祖先传过来的值,该函数的参数就是祖先传过来的value
val=><span>从祖先接收过来的值:{val}</span>
}
</MyContext.Consumer>
)
}
PureComponent
import React, { PureComponent } from 'react'
/*
PureComponent与Component的:
Component的2个问题:
1.只要执行setState(),即使不改变状态数据,组件也会重新render()
2.只要当前组件重新render(),即使子组件没有接收任何父组件的数据,
也会自从重新render子组件==>效率低
PureComponent:
底层重写了shoudComponentUpdate(),只有state或props数据有变化时才返回true
注意:只是进行state与props数据的浅比较,如果只是数据对象内部数据变化了,
则shoudComponentUpdate()会返回false,不更新页面
*/
export default class PureComponent extends PureComponent {
render() {
return (
<div>PureComponent</div>
)
}
}
render_props
import React, { Component } from 'react'
export default class Slot extends Component {
render() {
return (
<div>
<B render={name=><C name={name}/>}></B>
{/* <B>
<C/>
</B> */}
</div>
)
}
}
class B extends Component {
// B组件将name传给C组件
state = {name:'jack'}
render() {
const {name} = this.state
// 拿到B组件在Slot组件中的标签体内容
// console.log(this.props.children)
return (
<div>
{/* 调用父组件中传过来的render函数 */}
B:{this.props.render(name)}
</div>
)
}
}
class C extends Component {
render() {
return (
<div>接收从父组件B中传来的name:{this.props.name}</div>
)
}
}
错误边界
import React, { Component } from 'react'
export default class Parent extends Component {
state = {
hasError: ''
}
// 生命周期函数,当parent的子组件出现报错时,会触发getDerivedStateFromError调用,并携带错误信息
static getDerivedStateFromError(error) {
return { hasError: error }
}
componentDidCatch(){
console.log('此处常用于统计错误次数法,反馈给服务器,通知程序员修改')
}
render() {
return (
<div>
<p>Parent组件</p>
{this.state.hasError ? <h2>当前网络不稳定,请稍后再试</h2> : <Child />}
<div>子组件即使报错也能正常渲染</div>
</div>
)
}
}
class Child extends Component {
state = {
// 数据错误,本来是数组但是写出字符串使得遍历people的时候报错,
// 则需要用错误边界在父组件中进行拦截错误,使得页面正常渲染
people: ''
}
render() {
return (
<div>
{
this.state.people.map(v => {
return <li key={v.id}>v</li>
})
}
</div>
)
}
}
Fetch
//使用fetch发送网络请求(浏览器自带)
//不简化写法
fetch(url)
.then(
response=>{//该response拿到的是请求的状态
console.log('联系服务器成功了')
return response.json()//返回一个promise的对象,里面是需要获取的数据
},
error=>{
console.log('联系服务器失败了',error)
return new Promise(()=>{})
}
)
/* 如果response的返回值还是一个promise对象,
则该promise对象的成功/失败的回调会作为response的返回值,
若response的返回值为非promise对象,则response返回一个成功的回调 */
.then(
response=>{console.log('获取数据成功了',response)},
error=>{console.log('获取数据失败了',error)}
)
//简化版
try{
const response = await fetch(url)//response接受到的是请求的状态
const data = await response.json()//data接受到的是需要的数据
} catch(error){console.log('请求出错',error)}//用于捕获请求失败的原因