React入门:跟读官方快速入门教程(前端小白)

https://react.docschina.org/learn

快速入门

  1. 组件创建和嵌套
  2. 添加标签和样式
  3. 显示数据
  4. 渲染条件和列表
  5. 事件响应并更新界面
  6. 组件之间共享数据

基础概念

  • React是由组件构成
  • 一个组件是UI的一部分
  • 组件拥有自己的逻辑和外观
  • 组件可以是一个标签,也可以是整个页面
  • React组件是返回标签的JavaScript函数

React 组件必须以大写字母开头,而 HTML 标签则必须是小写字母。

export default 关键字指定了文件中的主要组件。如果你对 JavaScript 某些语法不熟悉,可以参考 MDN 和 javascript.info

事件处理

javascript 复制代码
function MyButton() {
  function handleClick() {
    alert('You clicked me!');
  }

  return (
    <button onClick={handleClick}>
      点我
    </button>
  );
}

注意!!onClick={handleClick} 的结尾没有小括号!。不要 调用 事件处理函数:你只需 把函数传递给事件 即可。当用户点击按钮时 React 会调用你传递的事件处理函数。

组件状态值传递:props

以井字棋为例

https://react.docschina.org/learn/tutorial-tic-tac-toe

别瞧不起这一个小的项目,我们跟着官方的教程实际上能学到很多代码设计和组织的思想。同时可以增强对react框架的理解。

游戏规则

  1. 初始状态:空棋盘

    \] \[ \] \[

    \] \[ \] \[

    \] \[ \] \[

  2. 玩家1(X)先手,选择一个空位放置 X

  3. 玩家2(O)在剩余空位放置 O

  4. 双方轮流落子,直到:

    • 有玩家连成一线 → 该玩家获胜
    • 所有格子填满且无人获胜 → 平局
(1)构建静态页面

包含井字棋 3X3 的静态方格显示。创建Square 组件表示一个单一的数字方格,Bord 组件则将九个数字方格组件集成到一起。

(2)交互式规则设计

当用户点击对应方格的时候,它显示的内容会发生改变。

当你单击它的时候,Square 组件需要显示"X"。在 Square 内部声明一个名为 handleClick 的函数。然后,将 onClick 添加到由 Square 返回的 JSX 元素的 button 的 props 中

javascript 复制代码
function Square({ value }) {
  function handleClick() {
    console.log('clicked!');
  }

  return (
    <button
      className="square"
      onClick={handleClick}
    >
      {value}
    </button>
  );
}
(3)状态管理state

我们希望Square能够记住用户的点击并显示正确的数据

javascript 复制代码
function Square() {
  const [value, setValue] = useState(null);

  function handleClick() {
    setValue('X');
  }

  return (
    <button
      className="square"
      onClick={handleClick}
    >
      {value}
    </button>
  );
}

每个 Square 都有自己的 state:存储在每个 Square 中的 value 完全独立于其他的 Square。当你在组件中调用 set 函数时,React 也会自动更新内部的子组件。

(4)游戏规则逻辑

对相邻的两次点击事件,交互显示"X","O"。

目前,每个 Square 组件都维护着游戏 state 的一部分。要检查井字棋游戏中的赢家,Board 需要以某种方式知道 9 个 Square 组件中每个组件的 state。

javascript 复制代码
import { useState } from 'react';

function Square({ value }) {
  return <button className="square">{value}</button>;
}

export default function Board() {
  const [squares, setSquares] = useState(Array(9).fill(null));
  return (
    <>
      <div className="board-row">
        <Square value={squares[0]} />
        <Square value={squares[1]} />
        <Square value={squares[2]} />
      </div>
      <div className="board-row">
        <Square value={squares[3]} />
        <Square value={squares[4]} />
        <Square value={squares[5]} />
      </div>
      <div className="board-row">
        <Square value={squares[6]} />
        <Square value={squares[7]} />
        <Square value={squares[8]} />
      </div>
    </>
  );
}

更改单击 Square 时发生的情况

Board 组件向下传递一个函数到 Square 组件,然后让 Square 在单击方块时调用该函数。我们将从单击 Square 组件时将调用的函数开始。调用该函数 onSquareClick

javascript 复制代码
function Square({ value, onSquareClick }) {
  return (
    <button className="square" onClick={onSquareClick}>
      {value}
    </button>
  );
}
(5)JavaScript数组
  • const copy = Squares.slice();// 不提供参数,复制整个数组
  • const getFirstN = (arr, n) => arr.slice(0, n); // 获取前n个元素
(6)传递函数而非直接调用

下面这条代码会出现错误❌:

javascript 复制代码
<Square value={squares[0]} onSquareClick={handleClick(0)} />

为什么会是这样呢?handleClick(0) 调用将成为渲染 Board 组件的一部分。因为 handleClick(0) 通过调用 setSquares 改变了棋盘组件的 state,所以你的整个棋盘组件将再次重新渲染。但这再次运行了 handleClick(0),导致无限循环:

// 当你渲染 Board 组件时:

  1. Board() 函数被调用执行
  2. 遇到 handleClick(0) → 立即执行函数
  3. handleClick(0) 内部调用 setSquares() 更新 state
  4. React 检测到 state 变化,安排重新渲染
  5. Board() 再次被调用执行
  6. 再次遇到 handleClick(0) → 立即执行
  7. 无限循环开始...

// ✅ 正确:函数传递(作为引用)
onSquareClick={() => handleClick(0)}

// 这创建了一个新函数,当点击时才执行 handleClick(0)

在 JSX 中传递事件处理函数时,应该传递函数引用或返回函数的函数,而不是函数调用的结果。React 组件在渲染时应该是"纯"的,不应该有直接改变 state 的副作用操作。

javascript 复制代码
import { useState } from 'react';

function Square({ value, onSquareClick }) {
  return (
    <button className="square" onClick={onSquareClick}>
      {value}
    </button>
  );
}

export default function Board() {
  const [squares, setSquares] = useState(Array(9).fill(null));

  function handleClick(i) {
    const nextSquares = squares.slice();
    nextSquares[i] = 'X';
    setSquares(nextSquares);
  }

  return (
    <>
      <div className="board-row">
        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />
        <Square value={squares[1]} onSquareClick={() => handleClick(1)} />
        <Square value={squares[2]} onSquareClick={() => handleClick(2)} />
      </div>
      <div className="board-row">
        <Square value={squares[3]} onSquareClick={() => handleClick(3)} />
        <Square value={squares[4]} onSquareClick={() => handleClick(4)} />
        <Square value={squares[5]} onSquareClick={() => handleClick(5)} />
      </div>
      <div className="board-row">
        <Square value={squares[6]} onSquareClick={() => handleClick(6)} />
        <Square value={squares[7]} onSquareClick={() => handleClick(7)} />
        <Square value={squares[8]} onSquareClick={() => handleClick(8)} />
      </div>
    </>
  );
}
(7)不变性
  • 不变性使复杂的功能更容易实现。
  • 使用具有所需变化的新副本替换数据
  • 不变性使得组件比较其数据是否已更改的成本非常低。
(8)交替落子

在Board组件中定义一个xIsNext状态,它接收一个布尔变量,控制状态显示。

为了避免一个数字方格多次点击出现状态漂移,我们在显示状态之前需要判断数字方格是否有数据存在。

(9)宣布获胜者

定义一个辅助函数calculateWinner,它接受 9 个方块的数组,检查获胜者并根据需要返回 'X'、'O' 或 null。由于我们知道所有的获胜情况,因此可以定义一个数组,通过将Squares传入的三个状态量与"获胜数组"中的情况进行比较,以此得到最终的赢家。

代码汇总
javascript 复制代码
import { useState } from 'react';

function Square({value, onSquareClick}) {
  return (
    <button className="square" onClick={onSquareClick}>
      {value}
    </button>
  );
}

export default function Board() {
  const [xIsNext, setXIsNext] = useState(true);
  const [squares, setSquares] = useState(Array(9).fill(null));

  function handleClick(i) {
    if (calculateWinner(squares) || squares[i]) {
      return;
    }
    const nextSquares = squares.slice();
    if (xIsNext) {
      nextSquares[i] = 'X';
    } else {
      nextSquares[i] = 'O';
    }
    setSquares(nextSquares);
    setXIsNext(!xIsNext);
  }

  const winner = calculateWinner(squares);
  let status;
  if (winner) {
    status = 'Winner: ' + winner;
  } else {
    status = 'Next player: ' + (xIsNext ? 'X' : 'O');
  }

  return (
    <>
      <div className="status">{status}</div>
      <div className="board-row">
        <Square value={squares[0]} onSquareClick={() => handleClick(0)} />
        <Square value={squares[1]} onSquareClick={() => handleClick(1)} />
        <Square value={squares[2]} onSquareClick={() => handleClick(2)} />
      </div>
      <div className="board-row">
        <Square value={squares[3]} onSquareClick={() => handleClick(3)} />
        <Square value={squares[4]} onSquareClick={() => handleClick(4)} />
        <Square value={squares[5]} onSquareClick={() => handleClick(5)} />
      </div>
      <div className="board-row">
        <Square value={squares[6]} onSquareClick={() => handleClick(6)} />
        <Square value={squares[7]} onSquareClick={() => handleClick(7)} />
        <Square value={squares[8]} onSquareClick={() => handleClick(8)} />
      </div>
    </>
  );
}

function calculateWinner(squares) {
  const lines = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6],
  ];
  for (let i = 0; i < lines.length; i++) {
    const [a, b, c] = lines[i];
    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
      return squares[a];
    }
  }
  return null;
}
相关推荐
绝世唐门三哥2 小时前
工具函数-精准判断美东交易时间
前端·javascript·vue.js
Moment2 小时前
如何一次性生成 60 种语气表达?RWKV 模型告诉你答案 ❗❗❗
前端·后端·aigc
踢球的打工仔2 小时前
typescript-null和undefined
前端·javascript·typescript
前端小蜗2 小时前
对不起,我很贱:老板还没催,我自己就统计《GitLab年度代码报告》
前端·javascript·人工智能
佛系打工仔2 小时前
绘制K线第三章:拖拽功能实现
android·前端·ios
cauyyl2 小时前
react 项目检查国际化配置脚本
前端·react.js·前端框架
康一夏3 小时前
React面试题,useRef和普通变量的区别
前端·javascript·react.js
前端 贾公子3 小时前
Monorepo + Turbo (6)
前端
冴羽3 小时前
2025 年 HTML 年度调查报告公布!好多不知道!
前端·javascript·html