React 入门文档

chaos 2 阅读 技术分享

基础概念

组件

一个方法就是一个React组件,组件是搭建应用的最基本单位,React的哲学是用一个一个组件组装成一个大的应用。例如下面的商品列表应用的组件结构图

组件图

JSX

jsx 是一种可以让你在js文件里用类似写 html 标签的方式写组件的语法。可以把他当作html标签来写。
React 组件必须以大写字母开头,这是为了区分组件与 html 标签
用className 来指定css类名,因为 class 是 js 关键字,在 js 文件中不能使用

export default function MyApp() {
  return (
    <div>
      <h1>欢迎来到我的应用</h1>
      <MyButton className="css-class-name" />
    </div>
  );
}

当需要在 JSX 中插入动态JS内容时需要用一对大括号 {} 来把内容括起来。{}内代码可以执行任意 js 表达式,注意只能是表达式,不能写语句。

React 没有引入特殊的语法来实现分支判断,循环等控制逻辑,而是完全利用 js 来做控制,你可以直接在 js 中通过控制生成目标 jsx(方式一) ,或者在 jsx 中用{}引入 js 语法来做逻辑控制(方式二)。在处理循环时需要注意一个特殊属性 key

// 方式 1
let content;  
if (isLoggedIn) {  
	content = <AdminPanel />;  
} else {  
	content = <LoginForm />;  
}  
return (  
	<div>  
		{content}  
	</div>  
);
//============================================
// 方式2 
<div>  
{isLoggedIn ? (  
	<AdminPanel />  
) : (  
	<LoginForm />  
)}  
</div>

//==============================================
// 列表循环

const products = [  
	{ title: 'Cabbage', id: 1 },  
	{ title: 'Garlic', id: 2 },  
	{ title: 'Apple', id: 3 },  
];

const listItems = products.map(product => {
	// 注意这里的key, 必须是这个列表内的唯一值。用于在列表内区分各个子组件,在列表数据源更新后,React 通过 key 来识别新旧组件是否是同一组件,应该替换还是更新它。
  return (
	  <li key={product.id}>
	    {product.title}
	  </li>
  )}
);
return (
  <ul>{listItems}</ul>
);

事件处理

在组件上用 onXxx={handlerFn} 来绑定事件处理函数,注意 handlerFn 结尾处没有(),不要调用事件处理函数,React 会在事件触发时,调用这个函数。函数被调用时会有一个参数 event,这个event 就是浏览器的事件对象。

function MyButton() {
  function handleClick(event) {
    console.log(event)
    alert('You clicked me!');
  }
  return (
    <button onClick={handleClick}>
      点我
    </button>
  );
}

form表单

对于原生html标签,比如表单元素 input,可以看作内置的组件来使用,它也可以通过 prop 来被父组件管理,与父组件通讯。

import { useState } from 'react'
export default function MyApp() {
  const [someValue, setValue] = useState('0')
  function onChange(event) {
    const v = Number(event.target.value)
    if(isNaN(v) || v < 0) return // 如果键盘输入内容不是大于等于0的数字就拦截输入
    // 只有符合要求的输入才回显到输入框
    setValue(event.target.value)
  }
  return (
    <input value={someValue} onChange={onChange} />
  )

内部状态

状态是组件内部的记忆,用于记住这个组件经过了哪些交互,反映当前状态到用户界面上。比如记录一个按钮被点击了几次。

// 首先,从 React 引入 useState
import { useState } from 'react';

function MyButton() {
// 声明一个状态变量和更新状态的函数
  const [count, setCount] = useState(0);
  function handleClick() {
  // 调用更新函数更新状态值
    setCount(count + 1);
  }
  return (
    <button onClick={handleClick}>
      Clicked {count} times
    </button>
  );
}

更新函数被调用后,React 会再次调用组件函数,渲染新的用户界面。
同一个组件的不同实例之间的状态是独立的,不会相互影响。

副作用

有时候应用需要与外部系统进行一些交互操作,比如上报数据,获取数据,控制非 React 组件之类的操作。这类操作不属于 React 内部的从状态到界面渲染的流程。React 把渲染页面之外的操作都称为 副作用(Effect) ,React 提供 useEffect 函数来执行这类操作,Effect 在渲染后执行。
useEffect 可以添加副作用,执行清理函数来清理副作用遗留下来的资源,还可以指定这个副作用什么时候执行。
这里来以一个聊天服务来举例

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

export default function ChatRoom() {
  // 添加副作用,第一个参数是副作用函数,第二个参数是依赖数组,用于决定副作用函数在什么时候执行
  useEffect(() => {
    // 这里可以与外部服务进行交互
    const connection = createConnection();
    connection.connect();
    // 这里返回一个清理函数,在组件卸载或者需要再次执行这个副作用之前执行,用于清理上次执行的副作用遗留资源
    return () => connection.disconnect();
  }, []); // useEffect 的第二个参数,依赖数组,当依赖数组内元素变化时,React 会再次执行这个副作用。当依赖数组是空时,只有在组件挂载时执行一次,组件卸载时执行一次清理函数。
  return <h1>欢迎前来聊天!</h1>;
}

组件接收外部数据

组件肯定不能只有内部状态,还需要从外部接收数据,通知(调用)外部父级组件。React 组件与外部通信的主要渠道是通过 prop。 prop 是一个对象,由 React 执行组件函数时传递给函数,prop 内可以包含任何合法的 js 值。

import { useState } from 'react';
export default function MyApp() {
  const [count, setCount] = useState(0);
  function handleClick() {
    setCount(count + 1);
  }
  function handleChange() {
    // 其他一些逻辑
  }
  return (
    <div>
      <h1>共同更新的计数器</h1>
      {/*  父组件传递 prop 到子组件的方式 */}
      <MyButton count={count} onClick={handleClick} />
      {/* 另一个组件,有不同的prop*/}
      <OtherComponent theme="dark" onChange={handleChange} />
    </div>
  );
}
function MyButton(props) {
  // 通过 prop 接收父级组件传过来的数据与方法,count 是数据用于显示, onClick 是方法用于通知(调用)父组件
  const { count, onClick } = props
  return (
    <button onClick={onClick}>
      点了 {count} 次
    </button>
  );
}

小结

React 的基础是组件,组件就是普通的函数,这个函数需要返回要渲染的 html 标签或者其他自定义组件。用JSX调用组件,通过 onXxx 属性绑定浏览器事件处理函数,然后用 useState 来保存和更新内部状态,用 prop 来与外部进行通讯,用Effect来与 React 外部环境进行交互。掌握了这些基础的概念就可以进行大多数的 React 应用开发了,任何复杂的应用都可以用 组件 ,事件,状态,属性,Effect 组装出来。

运行起来

这里以 vite 作为构建工具进行演示。用以下命令进行项目的初始化

npm create vite@latest my-app -- --template react

前端的基础 html 文件是必不可少的,最简单的 index.html 文件如下

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>my-app</title>
  </head>
  <body>
    <!-- 非常重要,入口容器,React 应用渲染到这里-->
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>

main.jsx 说明


// 引入React依赖
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx' // 这里是我们写好的组件

// 获取入口容器,然后创建根应用,进行渲染。后续React就会接管dom的更新和渲染,App组件内部的任何更新都会即时渲染到dom上,保持用户界面实时更新。
// StrictMode 是 React 内置组件,用于在开发期帮助发现副作用问题
createRoot(document.getElementById('root')).render(
  <StrictMode>
    <App />
  </StrictMode>,
)

通过 createRoot 来创建根应用,调用根应用的 render 方法将我们的组件渲染到根应用上。我们写的组件就会渲染到浏览器上,并可以接收浏览器交互事件,更新组件内部状态,再由 React 把内部状态实时渲染到浏览器,整个应用就运行起来了。