ReactJS
约 1816 个字 67 行代码 预计阅读时间 7 分钟
简介
也许你会在有的地方看到React是框架,但其实严格来说它是一个库,主要用于构建UI。
JSX
语法基础
Note
JSX and React 是相互独立的 东西。但它们经常一起使用,但你可以单独使用它们中的任意一个,JSX 是一种语法扩展,而 React 则是一个 JavaScript 的库。
JSX 可以看成是 JS 和 html标签的组合,可以添加JS表达式,不过需要用{}
括起来,不过,JSX 其实比html更加严格:
* 只能返回一个根元素,如果想要在一个组件中包含多个元素,需要用一个父标签把它们包裹起来
* 标签必须闭合,像 <img>
这样的自闭合标签必须书写成 <img />
* 使用驼峰式命名法给 所有 大部分属性命名,因为JSX最终会“编译”成JS,自然不能与JS的语法规范冲突
使用JS
使用引号传递字符串
这个很好理解,但是也可以使用变量,比如
export default function Avatar(){
const avatar="./imge.jpg";
return(
//avator是一个JS变量
<img>src={avator}</img>
)
}
使用大括号
大括号内任何JS语句都可以正常运行,比如:
const today=new Date()
export default function TodoList(){
return (
<p>Today is {today.getDay()}</p>
)
}
export default function TodoList(){
return (
<p style={
{
//注意这里也遵守了驼峰原则
backgroundColor: 'black',
color: 'white'
}
}>This is a graph</p>
)
}
组件
模块化
REACT 中的组件化思想,可以理解成把具有独立功能的 UI 进行了封装,也就是说组件同时包含了内容和逻辑。React 组件是常规的 JavaScript 函数,但组件的名称必须以大写字母开头,否则它们将无法运行!因为html标签都是小写,React组件都是大写,这样才可以区分它们,比如:
//不在同一行记得打括号,否则编译器会识别为return;
export default function Profile(){
return(
<>
<h5>Title</h5>
<p>This is a graph</p>
<img>src='./figure1.jpg'</img>
</>
)
}
export default
是JS标准语法,允许导出一个文件中的主要函数以便之后从其它文件引入。可以嵌套使用组件,但是不能嵌套定义,否则可能会导致一些意想不到的bug。
导出和导入
一个文件只能默认导出一个模块,其余的只能具名导出,在实际开发中一些团队会只使用一种风格(默认或者具名),下面是一个具名导出的例子:
//------Gallery.js
export function Profile(){
//...
}
//-------App.js
import {Profile} from './Gallery.js';
Props
React 组件使用 props(properties的缩写) 来互相通信。每个父组件都可以提供 props 给它的子组件,从而将一些信息传递给它,兄弟组件可以通过父组件“交流”。比如
export default function Profile(){
return (
<Avatar
person={{name:'Linying',imageId:'12345'}}
size={100}
/>
)
}
//现在Avator也可以读取上面那两个变量
function Avatar({person,size}){
// person和size可见
}
事实上props是组件唯一的参数,react组件接收一个props对象,当然通常不需要整个对象可以进行解构,就像上面那样,这个也是可以写默认值的。如果你需要把props原封不动的向下继续传递,可以使用展开语法:
html标签可以嵌套,React组件也是支持嵌套的,当你将内容嵌套在JSX标签中时,父组件将在名为children
的prop中收到该内容。
props并不总是静态的,它是反映某一时间点接收到的内容。但是,props是不可变的,所以只能通过创建新的实例来替换。
条件渲染
可以使用JS的if语句、&&和?:来选择性渲染JSX
渲染列表
- 先用filter过滤
- 首先把数据存储到数组
- 用map遍历数组得到JSX节点数组
- 把新数组用
<ul
包裹起来,然后返回它 这个其实还会报错,因为React要求给数组中的每一项都指定一个key,这个在对数组进行操作时非常重要
保持组件纯粹性
部分JS函数是纯粹的,这类函数被称为纯函数,纯函数仅执行计算操作,不做其他操作。纯函数通常具有如下特征: * 只负责自己的任务 * 输入相同,则输出相同
如果不这么做,在反复调用组件的过程中,某些东西的值会发生意外的改变
将UI视为树
和html一样,组件也是树状结构
添加交互
响应事件
添加事件处理函数
React允许你向JSX添加事件处理程序,事件处理程序是你自己的函数,这个就类似原生JS的事件监听器。在React中如需添加一个事件处理函数,需要先定义一个函数,然后将其作为prop传入合适的JSX标签,这个函数称作事件处理函数: * 通常在组件内部定义,当然,如果比较简单也可以内联 * 名称以handle开头,后跟事件名称
命名事件处理函数
可以给事件重新命名,按照惯例,事件处理函数应该以on开头,后跟一个大写字母,当组件支持多种交互时,可以根据不同应用程序命名事件处理函数
事件传播
事件处理函数还将捕获任何来自子组件的事件,通常,我们说事件会沿着树向上“冒泡”获“传播”
阻止传播
事件处理函数接收一个事件对象作为唯一参数,按照惯例,它通常被称作e,如果你想阻止一个事件到达父组件,需要用e
调用e.stopPropagation()
阻止浏览器默认行为
比如点击<form>
内部按钮会触发表单提交事件,默认情况下将重新加载整个页面,但是可以调用e.preventDefault()
来阻止这种情况
State
基础用法
可以用useState
Hook为组件添加状态(Hook是能让你的组件使用React功能的特殊函数)。useState
声明一个状态变量,返回一对值:当前状态,一个更新状态的函数。
为什么不能使用一般局部变量,局部变量无法再多次渲染中持久保存,每次渲染都相当于刷新,并且更改局部变量也不会触发渲染
state返回的状态变量极为特殊,比如,这是因为要下次渲染才会更新count:
这说明了state的快照性质,但是还有更特殊的例子,比如加一个定时器。 输出还是0,这充分说明一个sate变量的值永远不会在一次渲染的内部发生变化state是隔离且私有的
state是组件实例的内部状态,如果你渲染同一个组件两次,每个组件都会有完全隔离的sate,可以理解为封装好的私有变量,父组件也无法更改子组件的state
渲染和提交
React其实使用了虚拟DOM技术,即和这里的渲染相关,渲染始终是一次纯计算,只是计算出了页面有哪些元素,所以是虚拟的,如果发生改动才会设法修改
React发挥作用包含三个步骤: 1. 触发一次渲染 2. 渲染组件 3. 提交DOM
会有两种原因导致组件的渲染: 1. 组件的初次渲染:先createRoot再appendChild 2. 组件的状态发生了改变:应用最少得操作修改真是的DOM
把一系列的state更新加入队列
React会等到事件处理函数中的所有代码都运行完毕再处理你的state更新,这是你不能调用3次+1来实现+3的原因
这个要使用更新器函数,即在当前state的基础上更新,比如
更新state中的对象和数组
状态可以持有任何类型的JS值,包括对象,但是你不应该直接更改它,相反,你应该创建一个新的对象,或者用...
复制现有对象。同理,数组应该被视为只读,更新时需要创建新数组。即要修改state只能用相应的setState函数,这样才能让React知道state被修改了,从而触发渲染
状态管理
用State响应输入
React控制UI的方式是声明式的,不必直接控制UI的各个部分,只需要声明组件可以处于不同的状态,并根据用户的输入在它们之间切换。
- 定位组件中不同的视图状态
- 确定是什么触发了这些状态的改变
- 通过
useState
表示内存中的state
- 删除任何不必要的
state
变量 - 连接事件处理函数以设置
state
选择State状态
构建state
的原则:
* 合并关联的state
* 避免互相矛盾state
* 避免冗余的state
* 避免重复的state
* 避免深度嵌套的state
在组件间共享状态
对state进行保留和重置
迁移状态逻辑至Reducer中
使用Context深层传递函数
脱围机制
使用ref引用值
当你希望组件记住某些信息,但又不想触发新的渲染时,你可以使用ref:
可以通过ref.current
属性访问该ref的当前值,可以使用ref操作DOM元素
使用Effect进行同步
有些组件需要与外部系统同步,例如,可能需要根据React状态控制非React组件,Effect在渲染后运行一些代码。