【React】React基础入门

React 开发环境搭建

  • 安装 nodejs:官网下载最近 LTS 版本
  • 安装集成开发环境:vscode
  • 创建 react 项目:
    • npx create-react-app project-name
    • npm create vite@latest

React 核心语法:JSX

JSX是 Javascript 和 Html 的缩写,表示在js代码中编写html模版

JSX不能直接在浏览器中运行,需要bable进行编码成纯 js 代码

JSX 基础

JSX 识别 js 表达式

JSX 通过{}语法可以识别 js 表达式

可运行以下 js 表达式:

  • 字符串:{“hello world”}(支持模版字符串)
  • 变量/js 语句(运算符)
  • 函数调用
  • 方法调用

列表渲染

1
2
3
4
5
6
7
8
function List() {
  const list = ['python', 'java']
  return (
    <ul>
      list.map(item => <li key={item.id}>{item}</li>)
    </ul>
  )
}

简单条件渲染

对于简单的条件渲染,可通过逻辑与运算符或者三目运算符实现

1
2
3
4
5
6
7
8
9
const isLogin = true

function UserInfo({ username }) {
  return (
    <>
      isLogin ? <p>username: {username}</p> : <p>请登录</p>
    </>
  )
}

复杂条件渲染

复杂的条件渲染通过函数实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
const type = 0

function showTypedTemplate() {
  if (type === 0) {
    return <div>我是type0</div>
  }
  if (type === 1) {
    return <div>我是type1</div>
  }
  if (type === 2) {
    return <div>我是type2</div>
  }
}


func Article() {
  return showTypedTemplate()
}

React 事件绑定

语法

on + 事件名称 = {事件处理函数}

使用事件对象

事件处理函数中使用形参e

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
function App() {
  const handlerFunc = (e) => {
    console.log(e)
  }

  return (
    <div>
      <button onClick={handlerFunc}>click me</button>
    </div>
  )
}

export default App

传递参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
function App() {
  const handlerFunc = (e, name) => {
    console.log(e, name)
  }

  return (
    <div>
      <button
        onClick={(e) => {
          handlerFunc('cengdong')
        }}
      >
        click me
      </button>
    </div>
  )
}

export default App

React 组件状态管理

给 React 组件添加一个状态变量,让组件在不同状态下有不同表现

useState 基本使用

通过 useState hook 函数给组件添加一个计数器变量

1
2
3
4
5
6
7
8
9
fucntion App() {
  const [state, setState] = useState(0)

  const clickHandle = () => {
    setState(count + 1)
  }

  return <button onClick={clickHandle}>{state}</button>
}

useState 注意事项

状态变量是不可变的,只能通过setXXX函数,并且传入新的值来改变状态

直接修改状态不能引发视图更新

useState 案例

tabl 栏切换

  • 通过 useSate(type)记录 type 状态
  • 通过事件回调函数调用状态修改函数,传递被点击项的 type 值为参数
  • 通过 classnames 插件优雅实现类名切换

表单受控绑定

通过表单value属性绑定状态,通过onChange事件属性绑定状态同步的函数

1
2
3
4
5
6
7
function App() {
  const [content, setContent] = useState("")
  const clickHandle = () => {
    setContent("")
  }
  return <input type=text value={content} onChange={(e) => setContent(e.target.value)}/>
}

React 获取 Dom 元素

通过 useRef hook 获取 dom 元素从而调用 dom 元素的方法

useRef 基本用法

1
2
3
4
5
6
7
8
function App() {
  const inputRef = useRef(null)
  const publishHandle = () => {
    inputRef.current.focus()
  }

  return <input ref={inoutRef} onClick={publichHandle} />
}

注意事项

渲染完毕之后,dom 才可用

useRef 使用场景

  • 获取 Dom 元素,调用 Dom 元素的属性或者方法
  • 引用一个不需要被渲染的值
  • ref 不仅可以引用单个 dom 元素,也可以引用一个列表
  • 父组件要获取子组件 Dom 需要 forwardRef 函数做处理,因为 React 框架不允许

组件间通信

父传子

实现步骤:

  • 父组件传递数据:在子组件标签上绑定属性
  • 子组件接收数据:子组件通过props参数接受数据

props 说明

  • props 对象包含了父组件传递过来的所有数据
  • props 对象能传递任意类型的数据
  • props 对象是只读对象

children 说明

在子组件内部还可以嵌套内容,同样会给子组件传递数据,存在 props 对象children属性

子传父

  • 子组件调用父组件传递过来的函数
  • 通过函数参数传递数据给父组件

兄弟组件通信

兄弟组件之间通过状态提升传递数据

数据跨层通信

  • 使用 createContext 函数创建一个上下文对象 Ctx
  • 在顶层组件(App)中通过 Ctx.Provider 组件提供数据
  • 在底层组件(B)中通过 useContext hook 获取消费数据

useEffect

useEffect 是一个 React Hook,它允许你 将组件与外部系统同步

参考

useEffect(setup, dependencies?)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import { useEffect } from 'react'
import { createConnection } from './chat.js'

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234')

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId)
    connection.connect()
    return () => {
      connection.disconnect()
    }
  }, [serverUrl, roomId])
  // ...
}
  • setup:处理 Effect 的函数。setup 函数选择性返回一个 清理(cleanup) 函数。当组件被添加到 DOM 的时候,React 将运行 setup 函数。在每次依赖项变更重新渲染后,React 将首先使用旧值运行 cleanup 函数(如果你提供了该函数),然后使用新值运行 setup 函数。在组件从 DOM 中移除后,React 将最后一次运行 cleanup 函数。

  • 可选 dependencies:setup 代码中引用的所有响应式值的列表。响应式值包括 props、state 以及所有直接在组件内部声明的变量和函数。如果你的代码检查工具 配置了 React,那么它将验证是否每个响应式值都被正确地指定为一个依赖项。依赖项列表的元素数量必须是固定的,并且必须像 [dep1, dep2, dep3] 这样内联编写。React 将使用 Object.is 来比较每个依赖项和它先前的值。如果省略此参数,则在每次重新渲染组件之后,将重新运行 Effect 函数。如果你想了解更多,请参见 传递依赖数组、空数组和不传递依赖项之间的区别。

不同依赖项的区别

依赖项 副作用函数执行时机
没有依赖项 组件初次渲染+组件更新时
空数组 组件初次渲染
特定依赖项 组件初次渲染+依赖项变化

注意事项

  • useEffect 是一个 Hook,因此只能在 组件的顶层 或自己的 Hook 中调用它,而不能在循环或者条件内部调用。如果需要,抽离出一个新组件并将 state 移入其中。
  • 如果你 没有打算与某个外部系统同步,那么你可能不需要 Effect。
  • 当严格模式启动时,React 将在真正的 setup 函数首次运行前,运行一个开发模式下专有的额外 setup + cleanup 周期。这是一个压力测试,用于确保 cleanup 逻辑“映射”到了 setup 逻辑,并停止或撤消 setup 函数正在做的任何事情。如果这会导致一些问题,请实现 cleanup 函数。
  • 如果你的一些依赖项是组件内部定义的对象或函数,则存在这样的风险,即它们将 导致 Effect 过多地重新运行。要解决这个问题,请删除不必要的 对象 和 函数 依赖项。你还可以 抽离状态更新 和 非响应式的逻辑 到 Effect 之外。
  • 如果你的 Effect 不是由交互(比如点击)引起的,那么 React 会让浏览器 在运行 Effect 前先绘制出更新后的屏幕。如果你的 Effect 正在做一些视觉相关的事情(例如,定位一个 tooltip),并且有显著的延迟(例如,它会闪烁),那么将 useEffect 替换为 useLayoutEffect。
  • 如果你的 Effect 是由一个交互(比如点击)引起的,React 可能会在浏览器重新绘制屏幕之前执行 Effect。通常情况下,这样是符合预期的。但是,如果你必须要推迟 Effect 执行到浏览器绘制之后,和使用 alert() 类似,可以使用 setTimeout。有关更多信息,请参阅 reactwg/react-18/128。
  • 即使你的 Effect 是由一个交互(比如点击)引起的,React 也可能允许浏览器在处理 Effect 内部的状态更新之前重新绘制屏幕。通常,这样是符合预期的。但是,如果你一定要阻止浏览器重新绘制屏幕,则需要用 useLayoutEffect 替换 useEffect。
  • Effect 只在客户端上运行,在服务端渲染中不会运行。

使用场景

连接到外部系统

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import { useEffect } from 'react'
import { createConnection } from './chat.js'

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234')

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId)
    connection.connect()
    return () => {
      connection.disconnect()
    }
  }, [serverUrl, roomId])
  // ...
}

在自定义 Hook 中封装 Effect

Effect 是一种脱围机制:当你需要“走出 React 之外”或者当你的使用场景没有更好的内置解决方案时,你可以使用它们。如果你发现自己经常需要手动编写 Effect,那么这通常表明你需要为组件所依赖的通用行为提取一些 自定义 Hook。

例如,这个 useChatRoom 自定义 Hook 把 Effect 的逻辑“隐藏”在一个更具声明性的 API 之后:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function useChatRoom({ serverUrl, roomId }) {
  useEffect(() => {
    const options = {
      serverUrl: serverUrl,
      roomId: roomId,
    }
    const connection = createConnection(options)
    connection.connect()
    return () => connection.disconnect()
  }, [roomId, serverUrl])
}
Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计