初识react
1.React是用于构建用户界面的JavaScript库, 起源于Facebook的内部项目
2.视图层框架
React主要用于构建UI。你可以在React里传递多种类型的参数,如声明代码,帮助你渲染出UI、也可以是静态的HTML DOM元素、也可以传递动态变量、甚至是可交互的应用组件。
3.以组件的方式开发
React 提供了 state
和 props
两个主要的属性
1)state 就是组件里面的 React 的数据(也可以称之为状态)
2)props 是用于父子组件通信的(子组件可以通过 props 拿到父组件的数据或方法)
4.JSX 表达式
我们可以通过 【JSX 表达式】处理数据和 DOM 之间的关系,而不需要操作 DOM
5.虚拟 DOM
React 中采用虚拟 DOM 的机制;
1)在 React 中,我们会把定义好的JSX 标记最终转换为一个虚拟的 DOM,存在内存里
2)如果用户做了对DOM 可能产生影响的操作的时候,虚拟DOM 会把操作前后的数据进行对比,如果操作前后的数据有变化,就会把所有的变动然后统一操作一次 DOM ,如果发现操作前后的数据没有差异的话,就不会再去操作 DOM了;
3)虚拟DOM 有两大优势
1】一个是操作 DOM 前对数据进行对比,只有数据变化的时候才去操作DOM
2】它会整合 DOM 操作,可以减少 DOM 操作,提升性能
6.React 优点
1).声明式设计:React 使创建交互式 UI 变得轻而易举。为你应用的每一个状态设计简洁的视图,当数据变动时 React能高效更新并渲染合适的组件。
2)组件化: 构建管理自身状态的封装组件,然后对其组合以构成复杂的 UI。
3)高效:React通过对DOM的模拟,最大限度地减少与DOM的交互。
4)灵活:无论你现在使用什么技术栈,在无需重写现有代码的前提下,通过引入React来开发新功能。
一、jsx语法规则
可以从以下三个方面理解 JSX:
1)JSX 是一种 JS 扩展的表达式
2)JSX 是带有逻辑的标记语法,有别于 HTML 模版
3)并且支持样式、逻辑表达式和事件
jsx语法规则:
1.定义虚拟DOM时,不要写引号。
2.标签中混入JS表达式时要用{}。
3.样式的类名指定不要用class,要用className。
4.内联样式,要用style={{key:value}}的形式去写。
5.只有一个根标签
6.标签必须闭合
7.标签首字母
(1).若小写字母开头,则将该标签转为html中同名元素,若html中无该标签对应的同名元素,则报错。
(2).若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。
const myId = 'aTgUiGu'
const myData = 'HeLlo,rEaCt'
//1.创建虚拟DOM
const VDOM = (
<div>
<h2 className="title" id={myId.toLowerCase()}>
<span style={{color:'white',fontSize:'29px'}}>{myData.toLowerCase()}</span>
</h2>
<h2 className="title" id={myId.toUpperCase()}>
<span style={{color:'white',fontSize:'29px'}}>{myData.toLowerCase()}</span>
</h2>
<input type="text"/>
</div>
)
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('test'))
二、React创建自定义组件的两种语法
1.(1)面向过程 —— function式组件 —— rfc(函数组件又叫做 无状态组件
)
//1.创建函数式组件
function MyComponent(){
console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
/*
执行了ReactDOM.render(<MyComponent/>.......之后,发生了什么?
1.React解析组件标签,找到了MyComponent组件。
2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。
*/
(2)面向对象 —— class式组件 —— rcc(类组件又叫做 有状态组件
)
class MyComponent extends React.Component {
render(){
//render是放在哪里的?—— MyComponent的原型对象上,供实例使用。
//render中的this是谁?—— MyComponent的实例对象 <=> MyComponent组件实例对象。
console.log('render中的this:',this);
return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
}
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
/*
执行了ReactDOM.render(<MyComponent/>.......之后,发生了什么?
1.React解析组件标签,找到了MyComponent组件。
2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。
*/
2、class组件中this指向undefined问题
情形:①class组件中 ②事件处理方法中的this指向undefined
原因:①按钮单击触发,this不指向class对象
②JSX被编译为虚拟DOM对象,没有“事件源”概念
③JSX被编译为“严格模式”下的执行代码,事件处理函数为全局函数——严格模式下全局对象不是window而是undefined
解决方案:
方案1:利用 箭头函数
形式的 class实例
方法
f1 = ()=>{ }
方案2:利用箭头函数自身不绑定 this
的特点
onClick={ ()=>this.f1( ) }
方案3:利用 ES5
中的 bind()
方法,将事件处理程序中的 this 与组件实例绑定到一起
onClick={ this.f1.bind( this ) }
三、React中的三大属性
1.state
state有两个基本功能:
1)能够存储一定的值,从而能被react使用;
2)能够再它改变的时候被React监听到并且重新渲染
类组件
class Weather extends React.Component{
//初始化状态
state = {
isHot:false,
wind:'微风'
}
render(){
const {isHot,wind} = this.state
return <h1>
今天天气很{isHot ? '炎热' : '凉爽'}{wind}
</h1>
}
}
setState修改状态
- 状态是可变的
- 语法:this.setState({ 要修改的数据 })
- 注意:
不要直接修改 state 中的值,这是错误的!!!
- setState() 作用:1.
修改state
2.更新UI
//1.创建组件
class Weather extends React.Component{
//初始化状态
state = {isHot:false,wind:'微风'}
render(){
const {isHot,wind} = this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
}
//自定义方法————要用赋值语句的形式+箭头函数
changeWeather = ()=>{
const isHot = this.state.isHot
this.setState({isHot:!isHot})
}
}
//2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
函数组件
这里的setData的作用跟setState类似,但是它只能用来改变data的状态,需要改变的时候传入一个回调函数。函数的参数是之前的值,返回一个要改变的值。这个方法本质是需要传入一个新的对象,来改变React之前对象的值。
import {useState} from "react"
function FunctionComponent(){
const [data,setData] = useState("你要传入的初始值")
return (
<div>展示数据 {state}</div>
)
}
2.props
props功能在于组件间通信(父子组件传参)
注意:所有 React 组件都必须像纯函数一样保护它们的 props 不被更改,是只读的
类组件(父传子)
//父组件传值
class Father extends React.PureComponent{
render(){
return (
<Son value={"son"} />
)
}
}
class Son extends React.PureComponent{
render(){
return (
<div>this data is {this.props.value}</div>
)
}
}
类组件(子传父)
//子组件给父组件传值,此时往往需要父组件给子组件先传一个props函数,
//子组件通过调用传入的函数传值改变父组件的值
export default class Fa extends Component {
state = {faValue:'Fa1'}
changeFa = (value)=>{
this.setState(()=>{
return {faValue:value}
})
}
render() {
return (
<>
<h1>Fa's value is {this.state.faValue}</h1>
<Son changeFa={this.changeFa}/>
</>
)
}
}
export default class Son extends React.PureComponent{
changeValue = ()=>{
this.props.changeFa(this.inputRef.value)
}
render() {
return (
<>
<input type="text" placeholder={"请输入您的值"} ref={(el)=>{this.inputRef = el}}/>
<button onClick={this.changeValue}>change</button>
</>
)
}
}
函数组件(父传子)
function Fa(){
return (
<Son value={"son"} />
)
}
function Son(props){
return (
<div>this data is {props.value}</div>
)
}
函数组件(子传父)
function Fa(){
const [faValue,setFaValue] = useState("Fa1")
const changeFa = (value)=>{
setFaValue(value)
}
return (
<div>
<h1>Fa's value is {faValue}</h1>
<Son changeFa={changeFa} />
</div>
)
}
function Son(props){
const inputValue = useRef("")
//定义改变fa组件的值的函数
const changeFaValue = ()=>{
props.changeFa(inputValue.current.value)
}
return (
<>
<input type="text" placeholder={"请输入您要改变的值"} ref={inputValue}/>
<button onClick={changeFaValue}>change value</button>
</>
)
}
3.ref
ref是一个对象,它有一个current属性,可以对这个属性进行操作,用于获取DOM元素和保存变化的值。比如setTimeout的返回的ID,就可以把这个值放到ref中。为什么要放到ref中,因为更改ref的值,不会引起组件的重新渲染,因为值与渲染无关,它也不应该引起组件渲染。
怎么获取ref对象呢?调用useRef()函数和createRef()函数,它们返回ref对象。在组件的整个生命周期中,ref对象一直存在。
字符串形式
class Demo extends React.Component{
//展示左侧输入框的数据
showData = ()=>{
const {input1} = this.refs
alert(input1.value)
}
//展示右侧输入框的数据
showData2 = ()=>{
const {input2} = this.refs
alert(input2.value)
}
render(){
return(
<div>
<input ref="input1" type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
//渲染组件到页面
ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
回调引用
//创建组件
class Demo extends React.Component{
//展示左侧输入框的数据
showData = ()=>{
const {input1} = this
alert(input1.value)
}
//展示右侧输入框的数据
showData2 = ()=>{
const {input2} = this
alert(input2.value)
}
render(){
return(
<div>
<input ref={c => this.input1 = c } type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input onBlur={this.showData2} ref={c => this.input2 = c } type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
//渲染组件到页面
ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
使用useRef()
//组件挂载后,ref对象的current属性,就自动指向DOM元素
import React, { useRef } from "react";
const CustomTextInput = () => {
const textInput = useRef();
const focusTextInput = () => textInput.current.focus();
return (
<>
<input type="text" ref={textInput} />
<button onClick={focusTextInput}>Focus the text input</button>
</>
);
}
useRef是一个React Hooks,在函数组件中使用,它还可以接受一个参数,用于初始化useRef返回的对象的current属性。(const ref = useRef(initialValue);)
import React, { useRef } from "react";
const RenderCounter = () => {
const counter = useRef(0);
useEffect(() => {
counter.current = counter.current + 1;
});
return (
<h1>{`The component has been re-rendered ${counter} times`}</h1>
);
};
使用React.createRef()【类组件写法】
//创建组件
class Demo extends React.Component{
/*
React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的
*/
myRef = React.createRef()
myRef2 = React.createRef()
//展示左侧输入框的数据
showData = ()=>{
alert(this.myRef.current.value);
}
//展示右侧输入框的数据
showData2 = ()=>{
alert(this.myRef2.current.value);
}
render(){
return(
<div>
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input onBlur={this.showData2} ref={this.myRef2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
//渲染组件到页面
ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
使用React.createRef()【函数组件写法】
function CustomTextInput(props) {
// textInput必须在此处声明,以便ref可以引用它
let textInput = React.createRef();
function handleClick() {
textInput.current.focus();
}
return (
<div>
<input
type="text"
ref={textInput} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
四、事件处理
//创建组件
class Demo extends React.Component{
/*
(1).通过onXxx属性指定事件处理函数(注意大小写)
a.React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —————— 为了更好的兼容性
b.React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ————————为了的高效
(2).通过event.target得到发生事件的DOM元素对象 ——————————不要过度使用ref
*/
//创建ref容器
myRef = React.createRef()
myRef2 = React.createRef()
//展示左侧输入框的数据
showData = (event)=>{
console.log(event.target);
alert(this.myRef.current.value);
}
//展示右侧输入框的数据
showData2 = (event)=>{
alert(event.target.value);
}
render(){
return(
<div>
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
//渲染组件到页面
ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
五、react受控组件与非受控组件
1.受控组件中输入框的值由其change事件接管,做对应的数据劫持操作,例如可以对输入框的值进行合法输入校验(匹配正则),此时表单数据由 React 组件来管理(“受开发者控制”)。
import axios from "axios";
import React, { useState, useEffect } from "react";
function ControlledComponent() {
//创建一个state值绑定在输入框的value上;
const [userName, setUsername] = useState("小明");
//数据组装提交到后台
function doSumbit() {
const data = {
name: userName,
};
console.log(data)
axios("xxx", data);
}
useEffect(() => {
console.log(userName);
}, [userName]);
return (
<>
//输入框绑定change事件传入事件e
//change事件中得到输入框的值然后赋值改变state值
<input value={userName} onChange={(e) => {setUsername(e.target.value);} }></input>
<button onClick={doSumbit}>提交</button>
</>
);
}
export default ControlledComponent;
2.非受控组件则是同过ref直接获取输入框的值,只获取到最后状态的结果值,在输入过程中做不到对输入值的操作,此时表单数据将交由 DOM 节点来处理(“不受开发者控制”)。
import axios from "axios";
import React, { useRef } from "react";
function NotControlledComponent() {
const inputRef = useRef();
//通过ref.current.value获取输入框的值进行数据组装提交到后台
function doSumbit() {
const data = {
name: inputRef.current.value,
};
axios("xxx", data);
}
return (
<>
//创建一个ref实例绑定到输入框的ref上
<input ref={inputRef}></input>
<button onClick={doSumbit}>提交</button>
</>
);
}
export default NotControlledComponent;
六、class组件的生命周期
旧:
新:
七、消息订阅与发布机制
1.先订阅,再发布(理解:隔空对话的感觉)
安装第三方库pubsub-jsnpm add pubsub-js
利用钩子函数componentDidMount在组件挂载subscribe()函数进行订阅消息。import PubSub from 'pubsub-js' //引入
//订阅 subscribe()
componentDidMount(){
this.token = PubSub.subscribe('usersData',(_,data) => {
console.log('订阅消息:',data)
this.setState({...data})
})
}
//发布消息 publish()
PubSub.publish('usersData',{isFirst:false,isLoading:true})
//发送网络请求
axios.get(`https://api.github.com/search/users?q=${keyWord}`).then(
response => {
// this.props.UpdateAppState({isLoading:false,users:response.data.items})
PubSub.publish('usersData',{isLoading:false,users:response.data.items})
},
error => {
//this.props.UpdateAppState({isLoading:false,err:error.message})}
PubSub.publish('usersData',{isLoading:false,err:error.message})
}
)
2.适用于任意组件间通信
3. 要在组件的componentWillUnMount中取消订阅
//取消订阅 unsubscribe()
componentWillUnmount(){
PubSub.unsubscribe(this.token)
}
八、React中的数据绑定
注意:React中没有“指令”的概念,即无v-on、v-if、v-for、v-model.... |
①内容绑定:
<any>{表达式}</any>
②属性绑定:
<any 属性名={表达式}/>
③样式绑定:
<any style={ {color:'red'} }/>
④事件绑定:
<any onClick={表达式}/>
⑤双向数据绑定:
<input value={表达式} onChange={e=>setXxx(e.target.value)}/>
⑥条件渲染:
<any>{ 判定表达式 && (JSX) }</any> 等价于 if( ){ return (JSX) }
<any>{ 判定表达式 ? (JSX1) : (JSX2) }</any> 等价于 if( ){ return (JSX1) }else {return (JSX2)}
⑦列表渲染:
<any>{ arr.map( (e,i)=>(JSX) ) }</any>
九、路由 (v6)
v6 版本相比v5使用上发生了一些改变:
- v6版本移除了之前的
<Switch>
,引入了新的替代者<Routes>
<Routes>
和<Route>
配合使用,且必须用<Routes>
包裹<Route>
,当url
发生变化时<Routes>
会查看其所有子<Route>
元素找到最佳匹配并呈现组件<Route caseSensitive>
属性用于指定:匹配时是否区分大小写(默认为false
)
component 改为
element
- 路由重定向
Navigate
,代替之前的Redirect
<Route path='/' element={<Navigate to="/home"/>}/>
<Redirect path='/' to="/home" />
<NavLink/>
与<Link/>
类似,但增加了一个点击之后导航高亮的效果 ,它有一个默认的类名active
,通过这个类名可以修改高亮的颜色
步骤一:下载路由组件库
install i react-router-dom
步骤二:引用Link标签,替代<a>
标签进行跳转
步骤三:定义路由对应要跳转的组件
import { BrowserRouter,Link,Route, Routes} from "react-router-dom";
// link相当于 跳转标签,
// Route相当于容器
const Home=()=>{
return(
<h1>Home</h1>
)
}
const About=()=><h1>About</h1>
const Main=()=><h1>main</h1>
export default function Basic(){
return (
<BrowserRouter>
{/* // 如果项目需要路由,需要这个组件将项目包裹,只有在内部才能使用 */}
<ul>
<li>
<Link to='/'>home</Link>
</li>
<li>
<Link to='/about'>about</Link>
</li>
<li>
<Link to='/main'>main</Link>
</li>
</ul>
{/* 使用component组件渲染对应的组件 */}
{/* // react是包容性路由,满足条件都渲染,使用 exact精准匹配 */}
<Routes>
<Route path="/" element={<Home/>} exact></Route>
<Route path="/about" element={<About/>}></Route>
<Route path="/main" element={<Main/>}></Route>
</Routes>
</BrowserRouter>
)
}
4、Outlet
,useRoutes()
Route
可以嵌套使用,且可以配合useRoutes
配置路由表,通过<Outlet/>
来渲染匹配到的子路由
//一般的嵌套写法
<Routes>
<Route path='/home' element={<Home />}>
<Route path='news' element={<HomeNews />} />
<Route path="message" element={<HomeMessage />} />
</Route>
</Routes>
//使用使用useRoutes()写法
const element = useRoutes([
{
path: '/',
element: <Navigate to="/home" />
},
{
path: '/home',
element: <Home />,
children: [
{
path: 'news',
element: <HomeNews />
},
{
path: 'message',
element: <HomeMessage />
}
]
}
])
//Home文件
<Home>
<NavLink to="/home/news">news</NavLink>
<NavLink to="/home/message">message</NavLink>
</Home>
<Outlet/>//渲染路由规则匹配到的组件
5、路由传参
常见的组件路由传递参数的方式有三种,分别是通过params参数传递、利用search属性传递、利用state属性传递
1)通过params进行参数传递
一般来说,我们会使用类似于/${id}
的方式来传递值
<NavLink to={`/home/messages/details/${messageObj.id}/${messageObj.title}`}>xxx</NavLink>
虽然传递了多个参数,但是我们并不知道每个参数对应的key是什么,所以需要先规定好每个参数对应的key。
<Route path={'/home/messages/details/:id/:title'} component={Details}/>
然后在目标组件中使用this.props.match
来获取传递的参数
export default class Details extends Component {
state = {
details: [
{id:'01',comment:'你好,我的祖国'}
...
]
}
render() {
const {details} = this.state;
const {params} = this.props.match;
const detailofMessage = details.find((detailObj)=>{
return detailObj.id === params.id
})
return (
<ul>
<li>ID: {detailofMessage.id}</li>
<li>Title: {params.title}</li>
<li>Comment: {detailofMessage.comment}</li>
</ul>
)
}
}
上面是类组件方式,下面为函数组件
//在路由的path上配置:id
{
path: 'news/:id',
element: <HomeNews />
},
//传递
<NavLink to="/home/news/1/消息">news</NavLink>
//获取
function HomeNews() {
console.log(useParams());//{id: '1', title: '消息'}
const {id,title} = useParams()//获取url里携带的params参数
return (
<News>{id}{title}</News>
)
}
(2)组件路由通过search属性传递参数
种方式基本上和我们常见的在url地址上进行参数拼接是一样的,这种方式写法较为简单,只需要在 Link 标签中定义好key=value形式参数拼接,然后在实际的组件中,直接从prop属性中获取,调用querystring(或者其他处理字符串转对象的函数库)处理参数,并封装成对象返回即可
<NavLink to={`/home/messages/details?id=${messageObj.id}&title=${messageObj.title}`}>xxx</NavLink>
这种方式下的传参并不需要额外对路由进行配置
然后在实际组件中获取传递的参数(props.location.search
属性中把我们步骤一的参数都封装了进去,但是这种方式封装的参数是字符串形式的,我们并不能直接使用,而是要借助queryString
库来帮助我们把字符串解析成对象。)
import React, { Component } from 'react'
// querystring 是脚手架初始化的时候自带的函数库,能帮助我们分解和生成key=value形式的对象
import qs from 'querystring'
export default class Details extends Component {
state = {
details: [
{id:'01',comment:'你好,我的祖国'}
...
]
}
render() {
console.log('this.props',this.props)
const {details} = this.state;
const {search} = this.props.location;
// parse能将字符串转为key:value形式的对象
const messageObj = qs.parse(search.slice(1));
const detailofMessage = details.find((detailObj)=>{
return detailObj.id === messageObj.id
})
return (
<ul>
<li>ID: {detailofMessage.id}</li>
<li>Title: {messageObj.title}</li>
<li>Comment: {detailofMessage.comment}</li>
</ul>
)
}
}
上面是类组件方式,下面为函数组件
//传递search参数
<NavLink to="/home/news?id=1&title=消息">news</NavLink>
//接受search参数
function HomeNews() {
const [search,setsearch] = useSearchParams()//返回search参数 和 更新search参数的函数
const id = search.get('id')//通过get方法获取
const title = search.get('title')
console.log(useSearchParams());//[URLSearchParams, ƒ]
return (
<News>
<button onClick={()=>setsearch('id=2&title=更新search')}>点我更新search参数</button>
<hr />
{id}{title}
</News>
)
}
(3)通过state属性来进行参数的传递
这种方式传递的参数是不会在浏览器中显式的展示的,同时,即使刷新页面,路由子页面的数据还是不会消失。原因是react路由器帮我们对维护了history对象(history对象中又维护了location对象,所以也就有了state对象)
<NavLink to={{pathname:'/home/messages/details',state:{id:messageObj.id,title:messageObj.title}}}>xxx</NavLink>
这种方式下的传参也不需要额外对路由进行配置
然后在具体组件中获取传递的参数
export default class Details extends Component {
state = {
details: [
{id:'01',comment:'你好,我的祖国'}
...
]
}
render() {
const {details} = this.state;
const {id,title} = this.props.location.state;
const detailofMessage = details.find((detailObj)=>{
return detailObj.id === id
})
return (
<ul>
<li>ID: {detailofMessage.id}</li>
<li>Title: {title}</li>
<li>Comment: {detailofMessage.comment}</li>
</ul>
)
}
}
上面是类组件方式,下面为函数组件
//传递state参数
<NavLink to="/home/news" state={{id:1,title:'消息'}} >news</NavLink>
//接受state参数
function HomeNews() {
const { state:{id,title}} = useLocation()//连续解构赋值
return (
<News>{id}{title}</News>
)
}
6.编程式路由导航 useNavigate
代替v5中的this.props.history
默认是push
模式
export default function HomeNews() {
const navigate = useNavigate();
const jump = ()=>{
navigate('/home')
}
return (
<News>
<button onClick={jump}>点击跳转</button>
</News>
)
}
使用{replace:true}
就会变为replace
模式
navigate('/home', { replace: true });
也可以使用 navigate(-1)
传入一个数字来进行跳转
navigate(1)//传入数字
7.BrowserRouter与HashRouter的区别
1.底层原理不一样:
BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
HashRouter使用的是URL的哈希值。
2.path表现形式不一样
BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
HashRouter的路径包含#,例如:localhost:3000/#/demo/test
3.刷新后对路由state参数的影响
(1).BrowserRouter没有任何影响,因为state保存在history对象中。
(2).HashRouter刷新后会导致路由state参数的丢失!!!
4.备注:HashRouter可以用于解决一些路径错误相关的问题。
十、react hook
Hook 不能在 class 组件中使用 !!!只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用。只能在React 的函数组件或自定义的 Hook中调用 Hook
1.useState
用于定义变量和相关的更新变量的函数(将state按照相关性进行划分,这样有利于独立出自定义hook)
1: import React, { useState } from 'react';
2:
3: function Example() {
4: const [count, setCount] = useState(初始值);
5:
6: return (
7: <div>
8: <p>You clicked {count} times</p>
9: <button onClick={() => setCount(count + 1)}>
10: Click me
11: </button>
12: </div>
13: );
14: }
2.useEffect
相当于 componentDidMount 和 componentDidUpdate、componentWillUnmount
a.如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([]
)作为第二个参数。这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值。
useEffect(() => {
getList(current)
}, [])
b.采用传递第二个参数,判断是否需要重新执行
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新
c.组件即将挂载
useEffect( ()=>{
return ()=>{
//生命周期方法4 = 任意状态数据发生改变 + 组件即将卸载
}
} )
3.useRef
const refContainer = useRef(initialValue);
useRef
返回一个可变的 ref 对象,其 .current
属性被初始化为传入的参数(initialValue
)。返回的 ref 对象在组件的整个生命周期内保持不变。一个常见的用例便是命令式地访问子组件:
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
useRef
会在每次渲染时返回同一个 ref 对象,类似于class组件的this,可用于保存变量
function Timer() {
const intervalRef = useRef();
useEffect(() => {
const id = setInterval(() => {
// ...
});
intervalRef.current = id;
return () => {
clearInterval(intervalRef.current);
};
});
// ...
}
十一、React小知识:
React小知识:在JSX里如何创建一个不生成任何HTML元素的父元素? Vue.js中: 方法① <template></template>、 方法② <block></block> React中: 方法① <></> 方法② <React.Fragment></React.Fragment> 片段 |
十二、redux
安装
npm install -save redux
1. Action 的作用就是告诉状态管理器需要做什么样的操作
const add =()=>{
return {
type:"add",
data:id,
}
}
2.Reducer
这是一个确定状态将如何变化,以及更新状态的地方。
Reducer 是一个纯函数,接受 旧 state 和 action,根据不同的 Action 做出不同的操作,并返回新的 state 。即:(state, action) => state
/*
1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
*/
const initState = 0 //初始化状态
export default function countReducer(preState=initState,action){
// console.log(preState);
//从action对象中获取:type、data
const {type,data} = action
//根据type决定如何加工数据
switch (type) {
case INCREMENT: //如果是加
return preState + data
case DECREMENT: //若果是减
return preState - data
default:
return preState
}
}
3.Store
– 整个程序的状态state保存在Store中。
//import { createStore } from 'redux';
//const store = createStore(reducer);
/*
该文件专门用于暴露一个store对象,整个应用只有一个store对象
*/
//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
//暴露store
export default createStore(countReducer)
这就是 store, 用来管理 state 的单一对象,其中有三个方法:
1、store.getState(): 获取state ,如上,经过 reducer 返回了一个新的 state,可以用该函数获取。
2、store.dispatch(action) :发出 action,用于触发 reducer,更新 state,import { createStore } from 'redux'; const store = createStore(fn); const action = { type: 'change_input', value: 'Learn Redux', }; store.dispatch(action);
3、store.subscribe(listener):监听变化,当 state 发生变化时,就可以在这个函数的回调中监听。import { Component} from 'react'; import store from './store/index'; class List extends Component { constructor(props) { super(props); store.subscribe(this.storeChange.bind(this)); // store发生改变时,自动触发storeChange方法 } render() { return ( // .... ); } storeChange() { // store发生改变时,将自动触发 let newState = store.getState(); // 得到当前state状态,包含所有store属性 this.setState(newState); // 重新渲染 View } } export default List;
store.subscribe
方法返回一个函数,调用这个函数就可以解除监听。let unsubscribe = store.subscribe(() => console.log(store.getState()) ); unsubscribe(); // 解除监听
1)redux中如何进行异步操作?(借助异步中间件来进行异步处理)
redux-thunk中间件的使用方法:
2)connect用法(
连接 React 组件与 Redux Store)
导入:
import { connect } from 'react-redux'
使用:
connect( mapStateToProps , mapDispatchToProps )()
mapStateToProps
/*定义该参数后,组件就可以监听 Redux Store 的变化,任何时候只要store发生变化,
该函数就会被调用;
该函数必须返回一个纯对象,它会与组件的 props 结合,
而第二个参数 ownProps 被指定时,它代表传递到该组件的props,
且只要组件收到新的 props ,mapStateToProps 就被调用*/
const mapStateToProps = (state, ownProps) => {
return {
value: state.count //UI组件中需要什么参数,对象中就写哪些参数
}
}
mapDispatchToProps
/*如果mapDispatchToProps是一个函数,会得到dispatch和ownProps(容器组件的props对象)两个参数。作为函数,应该返回一个对象,该对象的每个键值对都是一个映射,定义了 UI 组件的参数怎样发出 Action*/
const mapDispatchToProps = (dispatch, ownProps) => {
return {
onIncreaseClick: () => dispatch({type: 'increase'}),
onReduceClick: () => dispatch({type: 'reduce'})
}
}