大家好,欢迎来到IT知识分享网。
1、React 框架简介
1.1、介绍
CS 与 BS结合:像 React,Vue 此类框架,转移了部分服务器的功能到客户端。将CS 和 BS 加以结合。客户端只用请求一次服务器,服务器就将所有js代码返回给客户端,所有交互类操作都不再依赖服务器。 客户端只有在需要服务器的数据时才会使用json通信一下,其他时间都在客户端利用js操作、暂存数据这样就极大减轻了服务器压力。
1.2、React 特性
- 虚拟DOM树
React 通过对 DOM 的模拟,最大限度地减少与DOM的交互。将网页的DOM树完全复制一份虚拟的 DOM 树放到内存中。
- 数据驱动
维护虚拟 DOM 树,当发现某些节点发生变化时,并不一定修改原来的 DOM 树(在网页中看到的每一个区域),比如某些分支可能会发生该改变时,首先会修改虚拟树中的这几个节点,而后将这几个节点与原节点对比。才会对真正发生改变的节点做出修改。
1.3、JSX文件
为了构建交互式用户界面,使用了 JSX。JSX 的完整版本是一个JavaScript语法扩展,它极大地简化了组件的创建。它支持HTML引用并使子组件渲染更容易。它本质上是一组 React 编写快捷方式。使用带有一些规则的 createElement 可以使源代码更具可读性和直接性。首先写jsx文件,运行时会先将所编写的jsx编译为js,编译完之后,将js文件运行在浏览器。
2、配置环境
2.1、安装 Git Bash
2.2、安装 Node.js
2.3、安装 create-react-app
打开 Git Bash,直接输入下面命令执行
npm i -g create-react-app
2.4、创建 React 项目,名为 React App
在目标目录(自己存放项目的目录)下右键打开 Git Bash,执行下面的命令
create-react-app react-app # react-app可以替换为其它名称
2.5、启动项目
进入目录,目录进到 react-app 这一层,打开 Git Bash,输入下面命令
npm start # 启动应用
3、组件(Component)
组件类似于一个 class,将一些 html、data(数据)、事件函数组合在一起成为一个组件
3.1、定义组件
定义好组件之后,需要将组件渲染出来,index.js 就是所有 js 的入口, 并引入React与Component组件。
// box.js 文件
import React, { Component } from 'react'; // 快捷键:imrc
// 引入React原因:将jsx编译为js
// 通过React.createElement()
class Box extends Component { // 快捷键:CC
state = { } //局部变量
// component类的函数,用来返回当前组件最后的渲染html结构
// render方法只能返回一个标签及所包含内容
render() {
return (
<h1> Hello world</h1>
);
}
}
export default Box;
// index.js 文件 import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import 'bootstrap/dist/css/bootstrap.css'; // 引入bootstrap库 import Box from './components/box'; // 引入box const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <Box /> // Box组件,见box.js </React.StrictMode> );
3.2、React Fragment 介绍与使用
- render()方法只能返回一个标签及所包含内容,想返回多个并列标签时需要包含在一个标签内。
- React.Fragment 组件能够在不额外创建 DOM 元素的情况下,让render()方法中返回多个元素。Fragments 允许将子列表分组,而无需向DOM添加额外标签。
理解起来就是在我们定义组件的时候,return里最外层包裹的div往往不想渲染到页面,那么就要用到Fragment组件了。
class Box extends Component { state = { } render() { return ( <React.Fragment> <h1> Hello world</h1> <button>left</button> <button>right</button> </React.Fragment> ); } }
3.3、在 jsx 中写 js 与 html 标签
jsx 中可以在任意地方定义 html 标签,但要注意,jsx 里的 html 标签中写 js 代码时都需要加 {} 括起来,{} 中只能写表达式。
render() {
return (
<React.Fragment>
<h1>{this.toString()}</h1> // html 标签内写js
<button className="btn btn-primary">left</button>
<button>right</button>
</React.Fragment>
);
}
toString(){
return `x: ${this.state.x}`;
// 或者
// const {x} = this.state; //ES6写法相当于const xxx = this.state.xxx
// return `x:${x}`;
}
3.4、设置样式
jsx文件下的html 标签中设置类名以用于 css 样式时,需要将 class =” “写为 className。 由于 jsx 下 html 标签与 js 语句混合,写 class 可能会与实际js中的同名类产生冲突。
className
return ( <React.Fragment> <h1>{this.toString()}</h1> <button className="btn btn-primary m-2">left</button> <button className="btn btn-success m-2">right</button> </React.Fragment> // m-2 为 bootstrap 中 margin=2 的简写方式 );
style
render() { return ( <React.Fragment> // style样式:第一层{}代表里面是表达式,第二层{}代表里面是对象,即样式变量的内容 <div style={
{ width: "50px", height: "50px", backgroundColor: "pink", color: "white", textAlign: "center", lineHeight: "50px", borderRadius: "5px", }}>{this.toString()}</div> <button className="btn btn-primary m-2">left</button> <button className="btn btn-success m-2">right</button> </React.Fragment> ); }
等价于:
styles = { width: "50px", height: "50px", backgroundColor: "pink", // css中所有 - 命名均改为驼峰命名法 } render() { return ( // 标签内 style={this.styele} 即可 <React.Fragment> <div style={this.styles}>{this.toString()}</div> <button className="btn btn-primary m-2">left</button> <button className="btn btn-success m-2">right</button> </React.Fragment> ); }
3.5、数据驱动改变 style
将 style 与 state(局部变量) 值相关联,通过改变 state 里的值来改变 style。当局部变量改变时,通过接口实现所有被这个值影响的组件都改变。
state = { x: 1, } getStyles() { let styles = { width: "50px", height: "50px", backgroundColor: "pink", color: "white", textAlign: "center", lineHeight: "50px", borderRadius: "5px", margin: '5px', }; if (this.state.x === 0){ styles.backgroundColor = 'orange'; // 数据驱动改变style } return styles; }
render() { return ( // 直接调用 getStyles()函数 <React.Fragment> <div style={this.getStyles()}>{this.toString()}</div> <button className="btn btn-primary m-2">left</button> <button className="btn btn-success m-2">right</button> </React.Fragment> ); }
3.6、渲染列表
使用 map 函数
遍历类写法需给每个标签元素定义唯一 key 属性,用来帮助React快速找到被修改的DOM元素
class Box extends Component { state = { x: 1, colors: ['red','green','blue'], // 定义渲染列表,这里用来修改div元素内容 } render() { return ( <React.Fragment> {this.state.colors.map(color => ( <div key={color}>{color}</div> // 这里建立div并将内容赋值为上述列表 ))} </React.Fragment> ); } }
3.7、Conditional Rendering
即利用逻辑表达式的短路原则:
render() { return ( <React.Fragment> {this.state.colors.length === 0 && <p> No colors</p>} // 即当 colors 有元素时无操作, 即当 colors 无元素时显示 No color {this.state.colors.map(color => ( <div key={color}>{color}</div> ))} </React.Fragment> ); }
3.8、绑定事件
class Box extends Component { handleClickLeft(){ console.log("click left",this); } handleClickRight(){ console.log("click right",this); } render() { //仅仅是绑定函数,而不是在渲染时就将返回值传过来,因此handleClickleft不加() return ( <React.Fragment> <div style={this.getStyles()}>{this.toString()}</div> <button onClick={this.handleClickLeft} className="btn btn-primary m-2">left</button> <button onClick={this.handleClickRight} className="btn btn-success m-2">right</button> </React.Fragment> } }
此时,输出的 this 不是 Box 类,而是 undifind。
如何使得方法里的 this 仍属于 Box 类: // 法一:箭头函数(推荐) // 法二: bind 函数
handleClickLeft=()=>{ // 法一:箭头函数 console.log("click left",this); } handleClickRight(){ console.log("click right",this); } render() { return ( <React.Fragment> <div style={this.getStyles()}>{this.toString()}</div> <button onClick={this.handleClickLeft} className="btn btn-primary m-2">left</button> <button onClick={this.handleClickRight.bind(this)} className="btn btn-success m-2">right</button> </React.Fragment> // 法二:bind函数 ); }
3.9、修改 state 里的值
直接 this.state.x-- 不会影响到页面div的显示的x值, 如果想要让 state 里的 x 的修改影响到render函数的话, 必须用 setState() 函数 (通过重新调用 render 修改 div 里 x 值)
class Box extends Component { state = { x: 1, } handleClickLeft = () => { this.setState({ // setState() 函数 x: this.state.x - 1 }); } handleClickRight = () => { this.setState({ // setState() 函数 x: this.state.x + 1 }); } render() { return ( <React.Fragment> <div style={this.getStyles()}>{this.toString()}</div> <button onClick={this.handleClickLeft} className="btn btn-primary m-2">left</button> <button onClick={this.handleClickRight} className="btn btn-success m-2">right</button> </React.Fragment> ); } }
3.10、通过按钮修改css属性
将 state 里的值赋值给某一样式的属性,通过按钮修改state 值从而修改 css 样式
class Box extends Component { state = { x: 10, // state值 } handleClickLeft = () => { this.setState({ x: this.state.x - 10 // setState() 修改 state值, 重新调用 render() 函数 }); } handleClickRight = () => { this.setState({ x: this.state.x + 10 // setState() 修改 state值,重新调用 render() 函数 }); } render() { return ( <React.Fragment> <div style={this.getStyles()}>{this.toString()}</div> <button onClick={this.handleClickLeft} className="btn btn-primary m-2">left</button> <button onClick={this.handleClickRight} className="btn btn-success m-2">right</button> </React.Fragment> ); } getStyles() { let styles = { width: "50px", height: "50px", backgroundColor: "pink", color: "white", textAlign: "center", lineHeight: "50px", borderRadius: "5px", margin: '5px', marginLeft: this.state.x, // state值赋值给 css 属性值 }; if (this.state.x === 0){ styles.backgroundColor = 'orange'; } return styles; } }
3.11、给事件函数添加参数
handleClickRight = (step) => { this.setState({ x: this.state.x + step }); }
handleClickRightTmp = () => { return this.handleClickRight(50); }
render() { return ( <React.Fragment> <div style={this.getStyles()}>{this.toString()}</div> <button onClick={this.handleClickLeft} className="btn btn-primary m-2">left</button> <button onClick={this.handleClickRightTmp} className="btn btn-success m-2">right</button> </React.Fragment> ); }
将 handleClickRight() 函数写为箭头匿名函数后等价于:
render() { // 绑定了一个调用含参 函数 handleClickLeft =(step)=>{ } 的匿名函数 return ( <React.Fragment> <div style={this.getStyles()}>{this.toString()}</div> <button onClick={()=>this.handleClickLeft(10)} className="btn btn-primary m-2">left</button> <button onClick={()=>this.handleClickRight(10)} className="btn btn-success m-2">right</button> </React.Fragment> ); }
综上,Box组件的构建步骤:
1、定义 state 变量,并使得数据驱动 style;
2、构造 handleClickLeft =(step)=>{ } 带参函数,利用 setState() 函数改变 state 值;调用setState()能够重新加载 render 函数,才可以对里面的 div 显示进行修改
3、给按钮的单击事件onClick绑定所写函数。这里绑定了一个调用含参函数 handleClickLeft =(step)=>{ } 的匿名函数,()=>this.handleClickRight(10)
class Box extends Component { // 1. 定义 state,并使得数据驱动style state = { x: 10, } // 2. 通过 handleClickLeft =(step)=>{ } 带参函数 与 setState() 改变state值 // 并能够重新加载 render 函数来对里面的 div 显示进行操作 handleClickLeft = (step) => { this.setState({ x: this.state.x - step }); } handleClickRight = (step) => { this.setState({ x: this.state.x + step }); } render() { // 3. 给事件绑定函数:通过 render 函数里,按钮事件绑定函数。 // 绑定了一个调用含参函数 handleClickLeft =(step)=>{ } 的匿名函数 return ( <React.Fragment> <div style={this.getStyles()}>{this.toString()}</div> <button onClick={()=>this.handleClickLeft(10)} className="btn btn-primary m-2">left</button> <button onClick={()=>this.handleClickRight(10)} className="btn btn-success m-2">right</button> </React.Fragment> ); } getStyles() { let styles = { width: "50px", height: "50px", backgroundColor: "pink", color: "white", textAlign: "center", lineHeight: "50px", borderRadius: "5px", margin: '5px', marginLeft: this.state.x, // 数据驱动 style }; if (this.state.x === 0){ styles.backgroundColor = 'orange'; } return styles; } }
4、Component 组件的组合与交互
4.1、【组合 Component 】组件的构建
组合多个上述定义的 Box 组件,形成 Boxes 组件,并完成 属性 值的传递。 <注:多个相同子组件时,每个子组件需要有唯一 key 值>
- 建立
Boxes类组件,内含多个Box组件
import React, { Component } from 'react'; import Box from './box'; class Boxes extends Component { // 1. 设置 state 变量,包括 Box 组件的唯一 key 值与 x 坐标值。 state = { boxes:[ {id: 1, x: 10}, {id: 2, x: 10}, {id: 3, x: 100}, {id: 4, x: 10}, {id: 5, x: 10}, ] } // 2. render 函数返回多个 box 组件,通过 map 列表,逐一建立并赋值多个 Box 组件 // 将 box.id 赋值给组件唯一 key,将 box.x 赋值给 Box 组件的 x render() { return ( <React.Fragment> {this.state.boxes.map((box)=>( <Box key = {box.id} // id x = {box.x} // 这里会自动找到 Box 组件里的 x 赋值并存储在 props 中 // 但仅仅是修改了x,并不会改变前端的显示 /> ))} </React.Fragment> ); } } export default Boxes;
【注】在react组件之间的通信是通过props属性来完成的,比如父组件需要将数据传递给子组件,那么组件在渲染子组件的时候,直接将数据作为子组件的属性传参。
- state 值传递:通过
props将Boxes定义的属性值返回传递给Box的
class Box extends Component { state = { // props类似于state,存储除key以外属于 box 的所有属性 // Boxes 建立的 Box 赋值的 x 存到了 props 里 // 通过 props 传递给了每个 Box x: this.props.x, } handleClickLeft = (step) => { this.setState({ x: this.state.x - step }); } handleClickRight = (step) => { this.setState({ x: this.state.x + step }); } render() { return ( <React.Fragment> <div style={this.getStyles()}>{this.toString()}</div> <button onClick={()=>this.handleClickLeft(10)} className="btn btn-primary m-2">left</button> <button onClick={()=>this.handleClickRight(10)} className="btn btn-success m-2">right</button> </React.Fragment> ); } getStyles() { let styles = { width: "50px", height: "50px", backgroundColor: "pink", color: "white", textAlign: "center", lineHeight: "50px", borderRadius: "5px", margin: '5px', marginLeft: this.state.x, }; if (this.state.x === 0){ styles.backgroundColor = 'orange'; } return styles; } toString(){ return `x: ${this.state.x}`; } } export default Box;
- 标签传递:通过
props将Boxes增加的Box.children子标签,传递给Box
// Boxes.jsx文件 render() { return ( <React.Fragment> {this.state.boxes.map((box)=>( <Box key = {box.id} x = {box.x}> <p>Box :</p> // 将 Box 的闭合标签写为双标签 <div>{box.id}</div> // 可在 Box 标签内增加其它标签,属性名为 Box.children </Box> // 并存储到了 props 中 ))} </React.Fragment> ); }
// Box.jsx 文件 render() { console.log(this.props); return ( <React.Fragment> {this.props.children[0]} // 通过 props 所存储的 children 将增加的标签传递给Box <div style={this.getStyles()}>{this.toString()}</div> <button onClick={()=>this.handleClickLeft(10)} className="btn btn-primary m-2">left</button> <button onClick={()=>this.handleClickRight(10)} className="btn btn-success m-2">right</button> </React.Fragment> ); }
- 方法传递1:React 子组件调用父组件的方法。
Box子组件调用Boxes父组件内的方法,依旧通过props。实现在 Box 组件中触发 onClick 事件后,在Boxes组件中删除key值对应的Box,即在Box标签内调用Boxes标签的方法
// Boxes.jsx 文件 // 1. Boxes.jsx 文件中写删除的方法 handleDelete = (id) => { // filter: boxes列表的元素依次判断,若表达式为true则留下,否则删掉 // 即若id不等留下来,相等删除 const newboxes = this.state.boxes.filter( (x)=>(x.id !== id) ); this.setState({ boxes: newboxes }) } render() { if(this.state.boxes.length === 0){ return <div className='alert alert-dark'>没有元素可以删除了!!!</div> } return ( // 2. 将所写删除方法定义为标签的 onDelete 属性传递给 Box(会存储在 props中) <React.Fragment> {this.state.boxes.map((box)=>( <Box key = {box.id} id={box.id} x = {box.x} onDelete = {this.handleDelete}> <p>Box :</p> <div>{box.id}</div> </Box> ))} </React.Fragment> ); }
// Box.jsx 文件 render() { return ( <React.Fragment> {this.props.children[0]} <div style={this.getStyles()}>{this.toString()}</div> <button onClick={()=>this.handleClickLeft(10)} className="btn btn-primary m-2">left</button> <button onClick={()=>this.handleClickRight(10)} className="btn btn-success m-2">right</button> // 3. Box 调用 Boxes 的删除方法 : // Box 中的 Button 的 onClick 事件,绑定匿名函数来调用含参的删除方法 <button onClick={()=>this.props.onDelete(this.props.id)} className='btn btn-danger m-2'> Delete </button> </React.Fragment> ); }
- 方法传递2:React 父组件调用子组件的方法。
仅能调用一个子组件方法,无法调用列表子组件
// 父组件 class Boxes extends Component { // 1. Boxes 父组件中写入 setChildRef = (ref) => { this.ChildRef = ref; } // 3. Boxes 父组件中写调用 Box 子组件的方法 handleReset = () =>{ this.ChildRef.handleRE() } render() { return ( <React.Fragment> // 4. 将父组件方法绑定onClick单击事件中,即可实现单击调用子组件的方法 <button onClick={this.handleReset} className='btn btn-primary'> Clear </button> {this.state.boxes.map((box)=>( // 2. Boxes 父组件的 Box 子组件标签内增加 ref 属性,并将 setChildRef 传递过来 <Box key = {box.id} ref={this.setChildRef} id={box.id} x = {box.x} onDelete = {this.handleDelete}> <p>Box :</p> <div>{box.id}</div> </Box> ))} </React.Fragment> ); } }
// 子组件 class Box extends Component { state = { x: this.props.x, } // 子组件中被调用的方法 handleRE = () =>{ this.setState({ x: 0 }); } render() { return ( <React.Fragment> ...... </React.Fragment> ); } }
5、组件使用
5.1、组件的生命周期
创建时(挂载阶段)
- 执行时机:组件创建时(页面加载时)
- 执行顺序:constructor() -> render() -> componentDidMount()
constructor()组件创建的时候,最先执行,主要是初始化数据,为事件处理程序绑定this
render():每次组件渲染触发,主要是渲染UI
componentDidMOunt():组件挂载完成,主要作用DOM操作,发送网络请求
更新时(更新阶段)
- 执行时机:1.setState() 2.组件收到props变了 3.forceUpdate()
- 说明:以上3中情况都会触发
- 执行顺序:render() -> componentDidUpdate()
render():每次组件渲染触发,渲染UI
componentDidUpdate():组件 状态更新完毕
预载时
- 执行时机:组件从页面消失
5.2、动态更新initialValue的值
[antd: Form.Item] defaultValue will not work on controlled Field. You should use initialValues o
原因:antd禁止在form.item下的组件使用默认属性
解决办法:删除defaultValue,在中使用initialValue={
{ parentId: parentId }}代替,如果要动态更新 parentId的值,又会导致下面的问题
React does not recognize the initialValue prop on a DOM element.
原因:不能通过这种方法更新parentId的值,想要更新props中的值,要通过form.setFieldsValue({ parentId: parentId })进行更改,
你不能用控件的 value 或 defaultValue 等属性来设置表单域的值,默认值可以用 Form 里的 initialValues 来设置。注意 initialValues 不能被 setState 动态更新,你需要用 setFieldsValue 来更新。
由上可知,表单中不能设置默认值,要想设置默认值必须通过form中的initial Values进行设置,但是其又不能动态更新,想要达到动态更新的效果必须使用form中的setFieldsValue进行更新,需要检测parentId的变化从而对parentId进行修改,固需要在useEffect中使用setFieldsValue
如果需要在父组件中得到子组件的form表单中的值,可以通过函数传参的方式,将子组件的form对象传递给父组件,这样父组件得到子组件的form对象,就可以通过getFieldValue得到表单的值了。
const [form] = Form.useForm() //将子组件的form传递给父组件,使得父组件可以取到子组件form中的categorieName的值 props.setForm(form) // 动态更新parentId的值 useEffect(() => { form.setFieldsValue({ parentId: parentId }) }, [parentId])
import React, { Fragment, useEffect } from 'react' import { Form, Input, Select } from 'antd' import { useForm } from 'antd/lib/form/Form'; const { Option } = Select; export default function AddForm(props) { const { categorys, parentId } = props const [form] = Form.useForm() console.log(parentId) // 动态更新parentId的值 useEffect(() => { form.setFieldsValue({ parentId: parentId }) }, [parentId]) return ( <Fragment> {/* initialValues不能被动态更新,需要使用form.setFieldsValues进行动态更新,将其放在useEffect中 */} <Form form={form}> <span>所属分类</span> <Form.Item name="parentId" rules={[ { required: true, message: 'Please select category!', }, ]} > {/* 表单中不能使用默认属性defaultValue,在form中使用initialValues{
{}}代替 */} <Select > <Option value='0'>一级分类</Option> { categorys.map(c => <Option key={c._id} value={c._id}>{c.name}</Option>) } </Select> </Form.Item> <span>分类名称</span> <Form.Item name="categoryName" rules={[ { required: true, message: 'Please input your categoryName!' } ]} > <Input placeholder="添加分类" /> </Form.Item> </Form> </Fragment > ) }
// 告警标签 // 动态更新initialValue值 changeMissLevel = () =>{ const{form} = this.props; form.setFieldsValue( { missLevel:'222' } ); } render(){ <Form.Item> {getFieldDecorator('missLevel',{ initialValue:this.state.missLevel })( <Select onChange={this.changeMissLevel} > <Option value= '1'>a</Option> <Option value= '2'>b</Option> </Select> ) } </> }
5.3、遍历、转换、非空判断、动态设置
- 遍历对象与数组
// 遍历对象 const object = {"a":"1","b":"2"}; for(let key in object){ console.log(key); console.log(object[key]); } // 遍历数组 const array = ["1","2","3"]; const array1 = [{"a":"1"},{"b":"2"}]; for(let key in array){ console.log(array[key]); } for(let key in array1){ console.log(array[key].a); console.log(array[key].b); }
- 对象动态值获取或作为键
// 动态值作为键 const obj = {"a":"1","b":"2"}; let ccc = 'c'; obj[ccc] = '3'; // 对象赋值 obj.d = '4'; // 对象获取值 let ddd = obj.d; // 对象动态值获取 const data = {"a":"1","b":"2"}; let aaa = 'a'; data[aaa+'']
- 对象与字符串的转换
// 对象转字符串 const obj = {"a":"1"} JSON.String(obj ); // 字符串转对象 String a = ""; JSON.parse(a); // join与split const aa = ["1","2"]; const bb = "1,2"; bb = aa.join(','); aa = bb.split(",");
- 非空判断
// 判断是否不为空 ifNotNull = (value:any) => { if( value != undefined && value != null && value != 'null' && value != 'NULL' && value != '[]' && value != '' && value != {} && value != [] && ) { return true; } else { return false; } } // 判断是否为空 ifNotNull = (value:any) => { if( value === undefined || value === null || value === 'null' || value === 'NULL' || value === '[]' || value === '' || value === {} || value === [] || ) { return true; } else { return false; } }
5.4、ant的Search组件,搜索框内容清空
<Search ref= "searchBar" placeholder="请输入开关名称或开关描述" enterButton="搜索" size="large" onSearch={this.inputSearch} allowClear /> // 清空操作 reset = () => { this.refs.searchBar.input.state.value=''; }
5.5、资源树结构选中取消
onTreeSelect = (selectKeys:any[], info: { props: {dataRef: any}}}) => { // 树节点取消触发 if(selectKeys[0] === undefined){ this.setState({ data:[], total:0 }); return; } // 选中 let restypeId = info.node.props.dataRef.restypeId; }
6、Hooks
6.1、useState
import { useState } from 'react';
- 使用 useState 的注意事项
- useState 向组件引入新的状态,这个状态会被保留在 react 中。
- useState 只有一个参数,这个参数是初始状态值,它可以是任意类型。
- useState 可以多次调用,意味着可以为组件传入多个状态。
- useState 返回值是一个数组,第一个元素就是我们定义的 state,第二个元素就是修改这个 state 的方法。接收 useState 的返回值使用数组结构语法,我们可以随意为 state 起名字。修改 state 的方法必须是 set + 状态名首字母大写构成,不按照约定写就会报错。
- useState 的参数也可以是一个函数,这个函数的返回值就是我们的初始状态。
function Counter() { const [count, setCount] = useState(0); return ( <div> <h3>{count}</h3> <button onClick={() => setCount(count + 1)}>+</button> </div> ); }
定义了一个状态 count,传入的参数为函数 () => 1,则 count 的初始值为函数的返回值1。当点击按钮时,调用修改方法 setCount 将 count 的值自增1,页面上计数器也会同步自增1。
function Counter() { const [count, setCount] = useState(() => 1); function handleCount(a) { setCount(a + 1); document.title = a; } return ( <div> <h3>{count}</h3> <button onClick={() => handleCount(count)}>+</button> </div> ); }
在点击按钮后,计数器显示数字变为了2,但页面的标题还是1。每次点击按钮后,页面的标题都比计数器显示数字少1。因此执行代码先执行了第5行的设置页面标题,才执行第4行的设置 count 值,看过上篇文章就很容易理解。
6.2、useEffect
import { useEffect } from 'react';
useEffect 这个 Hook 函数的主要作用就是将副作用代码添加到函数组件内。所谓的副作用代码就是 dom 更改、计时器、数据请求等。
使用场景:
- useEffect(() => {}) 这种写法代表两个生命周期函数 componentDidMount 和 componentDidUpdate。
- useEffect(() => {}, []) 这种写法代表生命周期函数 componentDidMount。
- useEffect(() => () => {}) 这种写法代表组件卸载之前 componentWillUnmount 和 componentDidUpdate 两个生命周期函数。
6.2.1、useEffect(() => {})
useEffect 钩子函数传入一个函数作为参数,代表组件在加载完成后和数据更新时都会调用传入的函数。
function Counter() { const [count, setCount] = useState(() => 1); function handleCount(a) { setCount(a + 1); document.title = a; } useEffect(() => { console.log('success'); }); return ( <div> <h3>{count}</h3> <button onClick={() => handleCount(count)}>+</button> </div> ); }
在页面加载完成和每次点击按钮让计数器自增后,控制台都会打印 success。即 useEffect Hook 实现了生命周期函数 componentDidMount 和 componentDidUpdate 的功能。
6.2.2、useEffect(() => {}, [])
useEffect 钩子函数传入一个函数和一个数组作为参数,代表组件在加载完成后会调用传入的函数。
function Counter() { const [count, setCount] = useState(() => 1); function handleCount(a) { setCount(a + 1); document.title = a; } useEffect(() => { console.log('success'); }, []); return ( <div> <h3>{count}</h3> <button onClick={() => handleCount(count)}>+</button> </div> ); }
这个案例和上一个案例不同之处在于 useEffect 钩子函数传入了一个空数组作为第2个参数,当页面加载完成时控制台会打印 success,点击按钮更新计数器后不再打印,即实现了生命周期函数 componentDidMount 的功能。
6.2.3、useEffect(() => () => {})
useEffect 钩子函数传入一个返回函数的函数作为参数,代表组件在数据更新时和卸载时会调用返回的函数。
function Counter(props) { const [count, setCount] = useState(() => 1); function handleCount(a) { setCount(a + 1); document.title = a; } useEffect(() => () => { console.log('success'); }, []); return ( <div> <h3>{count}</h3> <button onClick={() => handleCount(count)}>+</button> <button onClick={() => props.root.unmount()}>卸载组件</button> </div> ); }
这个案例中,点击按钮更新计数器和点击卸载组件时控制台都会打印 success,即实现了生命周期函数 componentWillUnmount 和 componentDidUpdate 的功能。
6.2.4、useEffect 第二个参数的使用
useEffect 钩子函数的第二个参数,正常添加空数组,代表的生命周期是 componentDidMount。即使我们修改了 state,useEffect 也只会调用一次。
function Counter() { const [count, setCount] = useState(() => 1); const [person, setPerson] = useState({ name: 'ccshen' }); function handleCount(a) { setCount(a + 1); document.title = a; } useEffect(() => { console.log('计数器改变'); }, [count]); // count 一旦发生改变,就会执行 useEffect return ( <div> <h3>{count}</h3> <h3>{person.name}</h3> <button onClick={() => handleCount(count)}>+</button> <button onClick={() => setPerson({ name: 'lisi' })}>更改person</button> </div> ); }
useEffect 第二个参数传递了 count,那么将会在 count 状态更新时才会执行传入的函数。
6.2.5、useEffect 的异步处理
function Counter() { function asyncFn() { setTimeout(() => { console.log('success'); }, 1000); } useEffect(() => { asyncFn(); }); return ( <div> </div> ); }
我们声明了一个异步函数,然后在 useEffect 中调用,预览正常,在页面加载完成1秒后打印 success。
当我们使用 async/await 时
function Counter() { function asyncFn() { setTimeout(() => { console.log('hahaha'); }, 1000); } useEffect(async () => { await asyncFn(); }); return ( <div> </div> ); }
在 useEffect 中如果使用了异步函数,那就需要定义一个自调用函数
function Counter() { function asyncFn() { setTimeout(() => { console.log('hahaha'); }, 1000); } useEffect(() => { (async () => { await asyncFn(); })(); }); return ( <div> </div> ); }
这时控制台就不报警告了。
遇到异步函数,我们需要在 useEffect 中添加一个自调用函数。
6.3、useContext
useContext 用于父组件向子孙组件传递数据,不需要再使用通过 props 从父组件向子组件逐级传递数据。如果组件多层嵌套,使用 props 来传值显得极其复杂,这时就需要使用 useContext。
import { useContext, createContext } from 'react';
首先定义一个 context 变量,用于存放当前上下文对象。将上下文对象的 Provider 作为父组件,通过 value 属性将要传递的值传给子孙组件。在子孙组件中就可以通过 useContext 获取到要传递的值。
const myContext = createContext(); // 当前上下文对象 function App() { const value = useContext(myContext); return ( <div>{value}</div> ); } export default function () { return ( <myContext.Provider value={100}> <div>hello world</div> <App /> </myContext.Provider> ); }
在 App 组件中我们获取到了父组件通过 context 传递来的 value 值。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/116638.html


