Skip to content

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>
    )
}
有一种特殊情况,比如使用内联CSS,需要赋值一个对象,就会产生双层大括号,比如
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原封不动的向下继续传递,可以使用展开语法:

function Profile({person,size,position}){
    return (
        <div>{...props}</div>
    )
}

html标签可以嵌套,React组件也是支持嵌套的,当你将内容嵌套在JSX标签中时,父组件将在名为children的prop中收到该内容。

props并不总是静态的,它是反映某一时间点接收到的内容。但是,props是不可变的,所以只能通过创建新的实例来替换。

条件渲染

可以使用JS的if语句、&&和?:来选择性渲染JSX

渲染列表

  1. 先用filter过滤
  2. 首先把数据存储到数组
  3. 用map遍历数组得到JSX节点数组
  4. 把新数组用<ul包裹起来,然后返回它 这个其实还会报错,因为React要求给数组中的每一项都指定一个key,这个在对数组进行操作时非常重要

保持组件纯粹性

部分JS函数是纯粹的,这类函数被称为纯函数,纯函数仅执行计算操作,不做其他操作。纯函数通常具有如下特征: * 只负责自己的任务 * 输入相同,则输出相同

如果不这么做,在反复调用组件的过程中,某些东西的值会发生意外的改变

将UI视为树

和html一样,组件也是树状结构

添加交互

响应事件

添加事件处理函数

React允许你向JSX添加事件处理程序,事件处理程序是你自己的函数,这个就类似原生JS的事件监听器。在React中如需添加一个事件处理函数,需要先定义一个函数,然后将其作为prop传入合适的JSX标签,这个函数称作事件处理函数: * 通常在组件内部定义,当然,如果比较简单也可以内联 * 名称以handle开头,后跟事件名称

命名事件处理函数

可以给事件重新命名,按照惯例,事件处理函数应该以on开头,后跟一个大写字母,当组件支持多种交互时,可以根据不同应用程序命名事件处理函数

事件传播

事件处理函数还将捕获任何来自子组件的事件,通常,我们说事件会沿着树向上“冒泡”获“传播”

阻止传播

事件处理函数接收一个事件对象作为唯一参数,按照惯例,它通常被称作e,如果你想阻止一个事件到达父组件,需要用e调用e.stopPropagation()

阻止浏览器默认行为

比如点击<form>内部按钮会触发表单提交事件,默认情况下将重新加载整个页面,但是可以调用e.preventDefault()来阻止这种情况

State

基础用法

可以用useStateHook为组件添加状态(Hook是能让你的组件使用React功能的特殊函数)。useState声明一个状态变量,返回一对值:当前状态,一个更新状态的函数。

为什么不能使用一般局部变量,局部变量无法再多次渲染中持久保存,每次渲染都相当于刷新,并且更改局部变量也不会触发渲染

state返回的状态变量极为特殊,比如,这是因为要下次渲染才会更新count:

console.log(count);  // 0
setCount(count + 1); // 请求用 1 重新渲染
console.log(count);  // 仍然是 0!
这说明了state的快照性质,但是还有更特殊的例子,比如加一个定时器。
setTimeout(() => {
    alert(count);
},3000)
输出还是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的基础上更新,比如

//这个会告诉React不仅仅是+1而是要马上更新state
//这个用到的是当前state真实值与变量无关
setScore(s => s+1);

更新state中的对象和数组

状态可以持有任何类型的JS值,包括对象,但是你不应该直接更改它,相反,你应该创建一个新的对象,或者用...复制现有对象。同理,数组应该被视为只读,更新时需要创建新数组。即要修改state只能用相应的setState函数,这样才能让React知道state被修改了,从而触发渲染

状态管理

用State响应输入

React控制UI的方式是声明式的,不必直接控制UI的各个部分,只需要声明组件可以处于不同的状态,并根据用户的输入在它们之间切换。

  1. 定位组件中不同的视图状态
  2. 确定是什么触发了这些状态的改变
  3. 通过useState表示内存中的state
  4. 删除任何不必要的state变量
  5. 连接事件处理函数以设置state

选择State状态

构建state的原则: * 合并关联的state * 避免互相矛盾state * 避免冗余的state * 避免重复的state * 避免深度嵌套的state

在组件间共享状态

对state进行保留和重置

迁移状态逻辑至Reducer中

使用Context深层传递函数

脱围机制

使用ref引用值

当你希望组件记住某些信息,但又不想触发新的渲染时,你可以使用ref:

const ref = useRef(0);
可以通过ref.current属性访问该ref的当前值,可以使用ref操作DOM元素

使用Effect进行同步

有些组件需要与外部系统同步,例如,可能需要根据React状态控制非React组件,Effect在渲染后运行一些代码。