自从react官方推出16.8版本出现hook以来,面向对象编程逐渐被函数式编程取代,所以本文章将全部以函数式编程,来讲解react的相关知识 最近要入职了公司用的react,不得不提前了解一下react,这里面东西可能不全,但是我后续慢慢学到会继续慢慢更新,大佬勿喷,对学完vue刚开始学react的小伙伴可能会有帮助
请结合我的另一篇vue基础知识对比学习juejin.cn/post/734119...
react基础语法
在聊hooks之前,我们先来学习一下react组件式开发的基本操作 JSX
react
一个简单的例子
import React from 'react';
function tset(props) {
return (
<div className="container"> //注意jsx必须只有一个跟标签 当然如果你不需要可以写成《》《/》这样的形式
<h1>Hello, World!</h1>
</div>
);
}
export default tset;
这就是一个简单的jsx模板 映入眼帘的很显然是return返回的标签。在react中,我们书写html都需要放到这个return语句中,实际上,整个jsx底层调用的是React.createElement,上面的代码其实就相当于
React.createElement("div", { className: "container" },
React.createElement("h1", null, "Hello, World!")
很显然,这样的写法显得十分麻烦,于是推出了jsx来做React.createElement语法糖。在jsx return里面,你可以几乎使用完全的html书写习惯,但是注意只是几乎,特别的,在html标签中class将不在使用而转为className,要始终记得,在return语句中,你书写的不是html,而是类似与js书写的标签,class转换为className,使用-连接的属性要变成大写
了解简单的html书写之后,我看一下样式如何添加上去
以上面的container为例,我们无法直接在jsx里面书写类似这样子
.container{
width:100%;
height:100%;
}
如果需要上样式的话,我们有两种常用的手段
1 我们使用<div style={container}> 使用内敛样式的形式,虽然jsx我们无法直接书写css代码,但是我们可以通过变量的形式保存css字符串,然后当作内联样式嵌入html
例如这样
const container= {
width: "100px",
height: "100px",
background: "red",
}; //注意用逗号哦,类似于对象的写法,时刻谨记,jsx中书写的是类似于css或者html的js
第二种办法
我们将css抽离出来,单独形成css文件,随后引入达到样式的书写
import "./css/App.css";,
这样我们直接写<div className="container"> 使用className的形式上样式即可
.container {
width: "100px";
height: "100px";
background: "red";
}
接下来我们了解一下在jsx中,如何使用类似于v-for 和v-if进行渲染
react
在vue里面,我们很容易就写出 这样的形式,之后vue就会帮我们循环渲染到模板中,但是在react却一样
<ul>
<li v-for="item in 20" :key="item.id"></li>
</ul>
以下面例子为例
let arr= [
{ id: "01", content: "js", age: 13 },
{ id: "02", content: "html", age: 15 },
{ id: "03", content: "css", age: 20 },
],
假设我们需要以ul li的形式渲染到页面上,我们肯定无法直接写成这样,没有对应的指令让我们这样书写
<ul>
<li ???="item in arr" :key="item.id"></li>
</ul>
正确的做法应该是
const show=arr.map((e)=>{
return (
<ul key={e.id}> //注意,身为父元素的ul必须加上唯一的id,否则控制台会报错
<li>{e.content}{e.age}</li>
</ul>
)
})
然后在jsx中,这样就可以实现循环渲染
function tset(props) {
return (
<div className="container">
{show}
</div>
);
}
我们不妨打印一下看看返回的是什么东西,不难看出,返回的结果个数组,数组里面是经过react.element构建来的
react
了解上述之后,我们应该不难想出,条件渲染是如何的
let show2 = show.length > 5 ? show2 : null;我们重新定义一个变量,来进行判断,当然这里我只采用了简单的三目,写成函数返回也是可以的
之后我们要展示的就不是show了而是show2
<div className="container">
{show2}
</div>
hook与事件
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
Hooks 的出现,首先能解决如下的一些问题:
- 告别令人疑惑的生命周期
- 告别类组件中烦人的 this
- 在类组件中,会存在 this 的指向问题,例如在事件处理函数中,不能直接通过 this 获取组件实例,需要修改 this 指向
- 告别繁重的类组件,回归前端程序员更加熟悉的函数
另外,Hooks 的出现,还有更加重要的一个信号,那就是整个 React 思想上面的转变,从"面向对象"的思想开始转为"函数式编程"的思想。这是编程范式上面的转变。
编程范式:
- 命令式编程:告诉计算机怎么做(How ),我们需要给计算机指明每一个步骤
- 面向过程
- 面向对象
- 声明 式编程:告诉计算机我要什么(What )
- 函数式编程
- DSL (领域专用语言,HTML、CSS、SQL)
声明式编程并不是新的产物,它是和命令式编程同期出现的。但是,早期更加流行命令式编程。不过随着近几年整个项目工程越来越复杂,以前的命令式编程就有点力不从心,所以现在慢慢开始流行声明式编程。
因此当你学习 Hooks 的时候,会发现突然多了一些以前不熟悉的概念,例如:纯函数、副作用、柯里化、高阶函数等概念。
当然,你可能好奇"面向对象"和"函数式编程"有什么区别,这里推荐一篇文章:
www.imaginarycloud.com/blog/functi...
Hook 就是 JavaScript 函数,但是使用它们会有两个额外的规则:
- 只能在函数最外层 调用 Hook。不要在循环、条件判断或者子函数中调用。
- 只能在 React 的函数组件 中调用 Hook 。不要在其他 JavaScript 函数中调用。
- 你可以使用
useEffect
来模拟componentDidMount
、componentDidUpdate
和componentWillUnmount
:useEffect
的回调函数会在组件挂载(相当于componentDidMount
)和更新(相当于componentDidUpdate
)后执行。- 如果
useEffect
的回调函数返回了一个清理函数,该函数会在组件卸载(相当于componentWillUnmount
)时执行。
第一个hook
react
在react16.8以后中,hook的出现大大提高了开发的效率,在react中也存者类似于vue的响应式,虚拟dom,单项数据流等等
在这里我们只简单介绍两个hook,后续在做补充
1 useState 详细看官方文档 https://react.docschina.org/reference/react/useState
useState 是一个 React Hook,它允许你向组件添加一个 状态变量。
·为组件添加状态
·根据先前的 state 更新 state
·更新状态中的对象和数组
·避免重复创建初始状态
·使用 key 重置状态
·存储前一次渲染的信息
基本用法
import { useState } from 'react'; 从react上引入 是不是有点类似于 import { ref } from 'vue'呢
而useState用法如下,useState传入你要定义的数据,然后会返回一个包括数据的变量和针对对应数据操作的方法函数
习惯上我们使用【age,setAge】这样的格式书写 age为你定义的数据28,setAge是针对当前数据进行操作的函数
const [age, setAge] = useState(28);
const [name, setName] = useState('Taylor');
const [todos, setTodos] = useState(() => createTodos());
不妨看一下结构出来的两个是什么
react
可以看出,拿到的是简单的数据,还有一个复杂的函数,我们不必关系他到底什么是,我们只需要会使用即可
接下来,我们做一个简单的例子,在那之前,我们简单了解一下react绑定事件的方式
react的事件绑定并不像vue一样采用@语法糖,而是有些类似于原生html写事件一样(内敛)
原生html我们可以 <button onClick="hancleClick(e)">点我修改age </button>
在react中,写法也是类似
<button onClick={(e) => changeAge(e)}>点我修改age </button>
注意注意,不难这样写<button onClick={changeAge(e)}>点我修改age </button> 如果你想要传递参数的话,不可以这样写的,需要写成上述的形式。当然,如果你没有参数需要传递<button onClick={changeAge}>可以写成这样
ok,我们尝试一下
import React, { useState } from "react";
function Tset(props) {
const [age, setAge] = useState(28);
function changeAge(val) {
setAge((e) => {
return e + val;
});
}
return (
<div className="container">
<h1>{age}</h1>
<button onClick={() => changeAge(1)}>点我+1</button>
</div>
);
}
export default Tset;
在上述例子中,我需要指出的是
{age}就是类似于vue的{{age}},将数据展示到页面上
然后changeAge接收一个参数val(1),在里面使用setAge对age进行操作,注意,不能直接对age进行操作
类似于
function changeAge(val) {
再次注意,请不要对数据直接进行操作,如果需要操作数据请使用setAge方法,在里面进行操作
age=age+val //控制台报错 Assignment to constant variable.
setAge((e)=>{
//return e++ //不能直接对数据进行操作
return e + 1; 可以写这样
})
如果你想将age变为一个固定的值,你也许可以这样
setAge(1) //这样的写法类似于 setAge(()=>1) 会将ag变为固定值
}
整体来说
setAge的返回值会替换掉原来的Age的值,所以这就是为什么请不要对数据进行直接操作的原因
在这里我需要指出,在hook之前,我们使用this.state = {
xxx : xxx 来进行赋值
}
使用this.setState({
xxx: 新值 进行修改
})
请注意,如果使用这样的写法,请永远记住,setSate是异步操作。
我们不妨打印一下看看这个setAge((e)=>{consle.log(e)})是什么东西
不难看到,数据已经变化了,但是打印出来的e却是上一个值
所以我们需要记住,setAge中传来的参数是上一个值,而非最新值
第二个Hook
副作用的概念
- 纯函数:一个确切的参数在你的函数中运行后,一定能得到一个确切的值,例如下面的例子:
js
function test(x){
return x * 2;
}
x => 2 ===> 4
x => 3 ===> 6
- 如果一个函数中,存在副作用,那么我们就称该函数不是一个纯函数,所谓副作用,就是指函数的结果是不可控,不可预期。
- 常见的副作用有发送网络请求、添加一些监听的注册和取消注册,手动修改 DOM ,以前我们是将这些副作用写在生命周期钩子函数里面,现在就可以书写在 useEffect 这个 Hook 里面
react
第二个hook是useEffect,用来处理副作用
简单介绍一下useEffect,useEffect传入两个参数,第一个参数是要执行的函数,第二个参数是依赖项
怎么说呢
如果不传入第二个依赖项
useEffect(() => {
// 书写你要执行的副作用,会在组件渲染完成后执行
document.title = `你点击了${count}次`;
console.log(`你点击了${count}次`);
});
这样写的话,就类似于vue中的update生命周期,每当组件进行更新或者页面发生变化的时候就会调用一次
如果
useEffect(() => {
// 书写你要执行的副作用,会在组件渲染完成后执行
document.title = `你点击了${count}次`;
console.log(`你点击了${count}次`);
},[]);
传入一个空的依赖项,那么这个就只会执行一次,有没有类似于 vue mounted的生命周期呢,
所以这种情况就可以用来发请求获取数据,如果没有传入依赖项,那么就会不断进行发送请求死循环了
如果
useEffect(() => {
// 书写你要执行的副作用,会在组件渲染完成后执行
document.title = `你点击了${count}次`;
console.log(`你点击了${count}次`);
},[count]);
如果传入了对应的依赖项,那么就有点类似于 vue的watch(()=>??,()=>{},{imider...首次监视})了,根据依赖项的变化进行执行,如果依赖项变化了就执行函数。
import { useState, useEffect } from "react";
function App() {
let [count, setCount] = useState(0);
useEffect(() => {
// 书写你要执行的副作用,会在组件渲染完成后执行
document.title = `你点击了${count}次`;
console.log(`你点击了${count}次`);
});
function clickhandle() {
setCount(++count);
}
return (
<div>
<div>你点击了{count}次</div>
<button onClick={clickhandle}>+1</button>
</div>
);
}
export default App;
另外,函数还可以有返回值,也是一个函数,用来处理函数产生的副作用,我们叫清理函数
看这样一种情况,在这里我们在副作用函数里写了一个计时器,我们很容易想到,不断的点击调用副作用函数,计时器也会发生高频触发的情况,显然这不是我们想要的
react
import { useState, useEffect } from "react";
function App() {
let [count, setCount] = useState(0);
useEffect(() => {
// 书写你要执行的副作用,会在组件渲染完成后执行
console.log(`你点击了${count}次`);
setInterval(() => {
console.log("点击了");
}, 1000);
}, [count]);
function clickhandle() {
setCount(++count);
}
return (
<div>
<div>你点击了{count}次</div>
<button onClick={clickhandle}>+1</button>
</div>
);
}
export default App;
想要解决这种情况
react
import { useState, useEffect } from "react";
function App() {
let [count, setCount] = useState(0);
useEffect(() => {
// 书写你要执行的副作用,会在组件渲染完成后执行
// //初次渲染执行一次
// //如果希望副作用只执行一次,传入第二个参数为空数组为依赖数组
console.log(`你点击了${count}次`);
let time = setInterval(() => {
console.log("点击了");
console.log("副作用函数执行");
}, 1000);
return () => {
~~~~~~~~可以看到,这里副作用函数返回了一个新的函数,我们称之为清理函数,用来处理因为副作用函数产生的副作用的
clearInterval(time);
console.log("清理函数执行");
//初次渲染不会执行
// //清理函数,清理函数会在渲染完成后,副作用函数执行前执行
};
// //在这个数组中写入了谁依赖项,谁变化才会引起副作用函数与清理函数
// //如果想只执行一次的话,写入空数组即可,例如发起网路请求的时候
}, [count]);
function clickhandle() {
setCount(++count);
}
return (
<div>
<div>你点击了{count}次</div>
<button onClick={clickhandle}>+1</button>
</div>
);
}
export default App;
自定义hook
除了使用官方内置的 Hook ,我们还可以自定义 Hook ,自定义 Hook 的本质其实就是函数,但是和普通函数还是有一些区别,主要体现在以下两个点:
- 自定义 Hook 能够调用诸如 useState 、useRef 等,普通函数则不能。由此可以通过内置的 Hooks 获得 Fiber 的访问方式,可以实现在组件级别存储数据的方案等。
- 自定义 Hooks 需要以 use 开头,普通函数则没有这个限制。使用 use 开头并不是一个语法或者一个强制性的方案,更像是一个约定。
App.jsx
react
import { useState } from 'react';
import useMyBook from "./useMyBook"
function App() {
const {bookName, setBookName} = useMyBook();
const [value, setValue] = useState("");
function changeHandle(e){
setValue(e.target.value);
}
function clickHandle(){
setBookName(value);
}
return (
<div>
<div>{bookName}</div>
<input type="text" value={value} onChange={changeHandle}/>
<button onClick={clickHandle}>确定</button>
</div>
)
}
export default App;
useMyBook
react
import { useState } from "react";
function useMyBook(){
const [bookName, setBookName] = useState("React 学习");
return {
bookName, setBookName
}
}
export default useMyBook;
生命周期
React Hooks 是 React 16.8 引入的新特性,它们允许你在不编写类组件的情况下使用状态和其他 React 特性。Hooks 并不直接提供传统类组件中的生命周期方法(如 componentDidMount
、componentDidUpdate
、componentWillUnmount
等),但你可以使用一些特定的 Hooks 来模拟这些生命周期行为。
以下是几个关键的 React Hooks,以及它们如何与生命周期方法相对应:
useState
:允许你在函数组件中添加状态。useEffect
:这是一个强大的 Hook,可以用来模拟大部分生命周期方法的行为。它可以在组件渲染后执行副作用,并且在组件卸载时清理这些副作用。useLayoutEffect
:与useEffect
类似,但它会在所有的 DOM 变更之后同步执行副作用。如果你需要读取 DOM 布局并同步触发重渲染,使用它。useCallback
:返回一个记忆化的回调函数,只有在依赖项改变时才会更新。useMemo
:返回一个记忆化的值,只有在依赖项改变时才会重新计算。useRef
:返回一个可变的 ref 对象,其.current
属性被初始化为传入的参数,类似于类组件中的createRef
。useImperativeHandle
:用于自定义使用ref
时公开给父组件的实例值。useContext
:用于访问 React Context。
虽然这些 Hooks 不能一对一地映射到生命周期方法,但你可以使用它们来实现类似的功能。例如,你可以使用 useEffect
来模拟 componentDidMount
、componentDidUpdate
和 componentWillUnmount
:
useEffect
的回调函数会在组件挂载(相当于componentDidMount
)和更新(相当于componentDidUpdate
)后执行。- 如果
useEffect
的回调函数返回了一个清理函数,该函数会在组件卸载(相当于componentWillUnmount
)时执行。
受控组件与非受控组件
了解之后我们来了解一下受控组件与非受控组件
受控组件
无论是学习 Vue ,还是 React,最重要的是要转换思想,这一步非常重要,往往也比较困难。
在以前 jQuery 时代,开发人员需要获取到 DOM 节点,然后进行操作。而在现代前端开发中,采用的是 MVVM 的模式,将视图和视图模型进行绑定,视图模型的改变,会自然的带来视图的改变。开发人员需要专注在视图模型上面。
因此,这里所谓的受控组件,本质上其实就是将表单中的控件和视图模型(状态)进行绑定,之后都是针对状态进行操作。
例子1
react
注意,在这里我=我们使用了inp标签,如果想要拿到inp标签的内容,我们仍然还是需要使用传统的dom.val进行拿到数据
import React, { useState } from "react";
function Tset(props) {
let [state, setState] = useState(""); //使用useState进行数据定义
注意,如果不对state值进行重新赋值的话,inp输入框里面是不会有你输入的东西的,这就是受控组件
function handleChange(e) { //inp的change方法
setState(() => {
return e.target.value;
});
如果全部注释掉的话,界面input输入框中无论你输入什么,都不会有任何显示,inp输入框的值收到state的显示,state有值,则输入框有值,state如果没有值,则输入框也没有值,且无法输入值
因此,这里所谓的受控组件,本质上其实就是将表单中的控件和视图模型(状态)进行绑定,之后都是针对状态进行操作。
// setState(() => {
// return e.target.value;
//});
}
function clickHandle() {
// 提交整个表单
console.log(`你要提交的内容为:${state}`);
}
return (
<div>
<input type="text"
value={state} //绑定state
onChange={(e) => handleChange(e)} />
<button onClick={clickHandle}>提交</button>
</div>
);
}
export default Tset;
例子2对用户输入的内容进行限制
react
import React, { useState } from "react";
function Tset(props) {
const [xiaoxie, setXs] = useState();
const [shuzi, setSz] = useState();
function changes(e) {
let name = e.target.name; //判断是对第几个输入框进行输入
switch (name) {
case "one":
//根据不同的输入框执行不同的操作
setXs(() => {
return e.target.value.toUpperCase(); //将用书输入的小写转化为大写,并且覆盖掉原来的值
});
break;
case "two":
//根据不同的输入框执行不同的操作
if (Number(e.target.value)) {
setSz(e.target.value);
break;
} else {
alert("请输入数字");
setSz((e) => e);
break;
}
}
}
return (
<div>
<input
type="text"
name="one"
onChange={changes}
value={xiaoxie}
placeholder="请输入小写"
></input>
<input
type="text"
name="two"
onChange={changes}
value={shuzi}
placeholder="请输入数字"
></input>
</div>
);
}
export default Tset;
例子3下拉列表
react
这里要注意的是,针对下拉列表,与input同理,受控组件收到设置的值的约束,如果需要改变值需要使用setValue方法
当然你可以直接写道onChange里面, onChange={(e) => setValue(e.target.value)}就像这样,直接将当前选择的值覆盖掉原来的
import React, { useState } from "react";
function Tset(props) {
const [value, setValue] = useState("css");
return (
<div>
<select
onChange={(e) => setValue(e.target.value)}
value={value} //绑定value
key="sss"
>
<option value="html">html</option>
<option value="css">css</option>
<option value="js">js</option>
</select>
</div>
);
}
export default Tset;
例子4多选框
react
针对多选框
import React, { useState } from "react";
function Tset(props) {
let [checks, setChecks] = useState([
{ content: "js", checked: true },
{ content: "html", checked: false },
{ content: "css", checked: true },
]);
function handelChange(index) {
setChecks((e) => {
!!!! 再次注意,请不要直接对数据进行操作,如果有必要,请先将数据生成一份保存下来,如下
const newChecks = [...e];
随后针对新的数据进行操作,操作完之后在完全覆盖掉原来的数据
newChecks[index].checked = !newChecks[index].checked;
return newChecks;
});
}
return (
<div>
//当然,如果你这样写不习惯,你也可以选择使用变量然后进行map操作将生成的jsx保存到变量然后放到此处
//这里使用了index当作对应的id,实际情况下,尽量还是给对应的数值设置唯一的id,避免特殊情况
{checks.map((e, index) => {
return (
<div key={index}>
<span>{e.content}</span>
<input
type="checkbox"
value={e.content}
checked={e.checked}
onChange={() => handelChange(index)}
></input>
</div>
);
})}
</div>
);
}
export default Tset;
非受控组件
大多数情况下,在 React 中推荐使用受控组件来对表单进行操作,这样能够对表单控件的数据进行一个统一管理。
但是在某些特殊情况下,需要使用以前传统的 DOM 方案进行处理,此时替代的方案就是非受控组件
首先介绍下类似于vue中的ref绑定dom的方法
在react中,使用 const fileRef = React.createRef();创建一个ref,类似于vue的 ref,仅作用于绑定,不少响应式!!
例子1
react
const fileRef = React.createRef();
<input ref={fileRef} type="file"></input>
通过fileRef可以拿到input的实例
非受控组件默认值
react
const inputCon = React.createRef();
<div>
{/* 一旦你用了 value,React 会认为你这是一个受控组件 */}
{/* 如果想要给默认值,使用 defaultValue */}
<input type="text" ref={inputCon} defaultValue="123"/>
<button onClick={this.clickHandle}>获取用户输入的内容</button>
</div>
文件上传
reac
import React from "react";
// 类组件
const uploadRef = React.createRef();
function clickHandle() {
console.log(uploadRef.current.files);
}
return (
<div>
<input type="file" ref={uploadRef}/>
<button onClick={clickHandle}>获取用户输入的内容</button>
</div>
)
export default App;
组件通信
最常用的莫过于props了,在初始模板我们很容易看到function中存在着props
react
import React from 'react';
function App(props) {
return (
<div>
</div>
);
}
export default App;
这个props就是为了接收参数而来的
相比vue的组件通讯,react似乎要舒服一点
父组件:
import React from "react";
import Child from "../component/child";
import { useState } from "react";
function App(props) {
const [dataList, setDataList] = useState([
{
id: "01",
name: "dz1",
age: 20,
},
{
id: "02",
name: "dz2",
age: 20,
},
{
id: "03",
name: "dz3",
age: 20,
},
]);
function AddDataList(val) {
setDataList((e) => {
~~~再次注意,请不要对元数据进行操作,如果有必要,请先保存一份然后操作后覆盖掉元数据
let neList = [...e];
neList.push(val);
return neList;
});
}
return (
<div>
//直接在子组件标签上写对应要传递的属性即可
<Child dataList={dataList} AddDataList={AddDataList}></Child>
</div>
);
}
export default App;
子组件:
import React from "react";
function child(props) {
let list = props.dataList;
//子组件通过props进行接收
let show = list.map((e) => {
//渲染数据展示出来
return (
<ul key={e.id}>
<li>
{e.name} {e.age}
</li>
</ul>
);
});
function add() {
//仿造子传夫情况,子组件获取一部分数据,传递给父组件,父组件调用自己的方法拿到子组件的数据
let obj = {
id: Math.random(),
name: `${Math.floor(Math.random() * 10)}asd`,
age: Math.floor(Math.random()) * 10,
};
props.AddDataList(obj);
}
console.log(props);
return (
<div>
{show}
<button onClick={add}>点我增加一条数据</button>
</div>
);
}
export default child;
react-router
react
index.js
import { BrowserRouter,HashRouter } from "react-router-dom"; //history 模式,HashRouter 哈希模式
<BrowserRouter>
<App />
</BrowserRouter>
使用模式的时候需要讲app外侧进行包裹,以开启指定的模式
路由配置 一
import { NavLink,Routes,Route } from "react-router-dom"; //导航按钮,导航配置,导航设置
<NavLink to="/home" className="navigation"> //navlink就是router-link,做导航按钮使用
主页
</NavLink>
<Routes>
<Route path="/home" element={<Home />} />
<Route path="/add" element={<Add />} />
<Route path="/about" element={<About />} />
<Route path="/detail/:id" element={<Detail />} />
<Route path="/edit/:id" element={<Add />} />
<Route path="/" element={<Navigate replace to="/home" />} />
</Routes>
方法二
新建route文件,新建route.js
import React from "react";
import { Navigate, useRoutes } from "react-router-dom"; //navigate 做重定向使用 useRoutes用来写路由配置
import Add from "../components/Add";
import Home from "../components/Home";
import About from "../components/About";
import Detail from "../components/Detail";
import Emil from "../components/Emil";
function Route(props) {
return useRoutes([
{
path: "/home",
element: <Home />,
},
{
path: "/add",
element: <Add />,
},
{
path: "/about",
element: <About />,
children: [
{
path: "emil",
element: <Emil></Emil>,
},
],
},
{
path: "/detail/:id",
element: <Detail />,
},
{
path: "/edit/:id",
element: <Add />,
},
{
path: "/",
element: <Navigate replace to="/home"></Navigate>,
},
]);
}
export default Route;
在app.jsx里
import RouteConfig from "./route/route"; 引入刚刚配置的route文件
使用
<RouteConfig></RouteConfig> 表示路由显示的位置
子路由展示与编程式与导航参数导航
import { NavLink, useNavigate, Outlet } from "react-router-dom";
NavLink //导航组件 useNavigate返回一个路由器,通过路由器控制编程路由 outlet 子路由展示区域
let navgite = useNavigate();
function toEmli() {
navgite("/about/emil"); //进行路由跳转
}
<h1 className="page-header">使用说明</h1>
<p>学习reatc-router</p>
<span onClick={toEmli}>点击进入emil</span>
<NavLink to="/about/emil">进入emli</NavLink>
{/* 用于在父组件子路由的展示 */}
<Outlet></Outlet>
路由参数与获取路由参数
使用编程时导航进行路由跳转,同时增加路由参数
navigate("/home", {
state: {
alert: "学生删除成功",
type: "info",
},
});
import { useLocation } from "react-router-dom";
//useLocation 返回路由信息
let location = useLocation();
location.state //获取路由传来的参数
//导航组件传递参数
<NavLink to={`/detail/${e.id}`}>详细</NavLink>
{ 注意在路由配置里面的写法
path: "/detail/:id",
element: <Detail />,
},
react-redux+react-toolkit
react
安装
npm i @reduxjs/toolkit react-redux
index.js 配置
import { Provider } from "react-redux"; 引入provider包裹app,并且将store传递下去
import store from "./store/store";
root.render(
<Provider store={store}>
<App />
</Provider>
);
store.js
import { configureStore } from "@reduxjs/toolkit"; //引入创建仓库
import toDoList from "./todoListSlice"; //引入仓库切片
export default configureStore({
//传入配置对象
reducer: {
todo: toDoList,
},
});
todoList.js
import { createSlice ,createAsyncThunk} from "@reduxjs/toolkit"; //创建切片
//创建切片
//createAsyncThunk 写异步操作发请求的
export const toDoList = createSlice({
name: "todoList", //类似仓库名,命名空间
initialState: {
//书写仓库数据
list: {
name: "asd",
age: 20,
},
},
//reducers 修改仓库里面的数据方法,类似于action
reducers: {
/**
*
* @param {*} state 仓库数据
* @param {*} val 传递过来的数据
*/
add: (state, { payload }) => {
//允许直接修改数据
state.list[0].age = state.list[0].age + payload;
console.log("state", state, "val", payload);
},
changes2: (state, { payload }) => {
console.log(payload);
state.list.push(payload);
},
},
});
console.log(toDoList.reducer, toDoList.actions);
// 切片上的reducer是一个函数,会提供仓库的数据,仓库的方法在actions中
export default toDoList.reducer;
app.js 使用仓库数据
import { useSelector } from "react-redux";
function App() {
let { list } = useSelector((state) => {
console.log("state", state); //拿到仓库的数据
return state.todo;
});
const list2 = list.map((e) => {
return (
<ul key={e.name}>
<li>{e.name}</li>
<li>{e.age}</li>
</ul>
);
});
使用action方法
import { useDispatch } from "react-redux";
import { add, changes2 } from "./store/todoListSlice";
let dispatch = useDispatch(); 调用useDispatch返回一个dispatch方法,使用这个方法调用函数
function changeAge(val) {
dispatch(add(val));
let obj = { name: Math.random(), age: val * 10 };
dispatch(changes2(obj));
}
例子
简易版学生管理系统