【学习笔记】React官方Tutorial: Tic-Tac-Toe - 上

教程网址:https://react.dev/learn/tutorial-tic-tac-toe#setup-for-the-tutorial

-在 React 中,组件是一段可重用的代码,代表用户界面的一部分。组件用于渲染、管理和更新应用程序中的 UI 元素。

-给函数加上export关键字,使此函数可在此文件外部访问。

-给函数加上default关键字,告诉使用该文件的代码的其他文件(如index.js),该函数是此文件的主要函数/top-level component。

-JSX 元素是 JavaScript 代码和 HTML 标签的组合。

-index.js是组件与Web浏览器之间的桥梁,导入React包,导入React DOM包,导入style.css,导入App.js,并且把所有的东西组合为final product,注入到index.html。

-React组件需要返回单个 JSX 元素,而不是多个相邻的 JSX 元素。使用Fragments<></>)包装多个相邻的 JSX 元素来返回。

-定义Square组件

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

-定义了一个名为Square的组件,该组件渲染一个带有类名“square”的按钮元素。

-一旦你定义了一个组件(比如Square),你可以在任何其他组件内部通过JSX语法<Square />来引用它。这就像是在HTML中使用自定义标签一样,React会处理这些标签,每次使用<Square />时,React都会创建一个新的Square实例,并根据Square函数中定义的规则渲染相应的DOM元素。

-代码重用、结构清晰、封装性好

 -Props:是父组件向子组件传递数据的方式。可以把props想象成一个组件的配置参数,它们是单向从父组件传递到子组件的。子组件接收这些参数后可以使用它们进行渲染或其他逻辑处理。

function Square({ value }) {
  return <button className="square">{value}</button>;
}
<div className="board-row">
        <Square value="1" />
        <Square value="2" />
        <Square value="3" />
      </div>

通过props生成具备不同value的square

 -为格子添加点击事件:

function Square({ value }) {
  function handleClick() {
    console.log("clicked!");
  }
  return (
    <button className="square" onClick={handleClick}>
      {value}
    </button>
  );

-直接从传入的props对象中提取value属性

-onClick={handleClick}:是React特有的,用来在按钮被点击时调用handleClick函数。这里的{}是JavaScript表达式的标识,允许在JSX中嵌入JavaScript代码。

-useState的使用:

import { useState } from "react";

function Square({}) {
  const [value, setValue] = useState(null);

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

- 使用import { useState } from 'react';从React库中引入useState函数。这是一个React Hook,允许在函数组件中添加状态。

- 对于每个Square组件的实例,React调用useState(null)。这是组件函数首次执行的一部分。在这一步,useState设置初始状态null并返回两个值:当前状态value和更新这个状态的函数setValue

- useState(null)调用了useState钩子,并将初始状态设置为null。这意味着变量value在组件第一次渲染时的值将是null

- useState返回一个包含两个元素的数组:当前状态的值和一个更新这个状态值的函数。通过数组解构,我们得到value(当前的状态值)和setValue(一个用来更新value的函数)。

- 调用setValue来更新value值,触发组件重新渲染,反应新的状态(React的核心特性之一:基于状态的UI自动更新。)

- 每个square都有自己的state,每个suqare保存的value也互相独立

- React的纤程(Fiber)架构

React内部使用纤程(Fiber)架构来管理组件树和状态。每个组件实例对应一个Fiber节点,这个节点包含了组件的状态、props、回调函数等信息。这使得React可以非常精确地跟踪和更新组件状态,即使组件是动态生成的并且数量众多。

- React DevTools 检查 React 组件的 props 和状态。

- Lifting state up:

相比让board一个个去询问每个square的state,不如让Board组件保存整个游戏的state,然后通过让board传递一个prop来告诉每个square该展示什么内容。

将状态提升到父组件:要从多个子组件收集数据,或让两个子组件相互通信,需要在其父组件中声明共享状态(子组件不再需要状态)。父组件可以通过 props 将状态和修改状态的函数如setState传递回子组件,使子组件彼此和父组件之间同步。

使用从父组件传递下来的函数在子组件中更新状态,这会反过来更新父组件的状态,并通过新的props更新所有相关的子组件。

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)} />
            ······

Board组件管理所有方格的状态,并通过props向每个Square组件传递状态和事件处理函数。所有Square共享来自同一个父组件的状态。

-闭包closure:JavaScript 支持闭包,这意味着内部函数(例如handleClick)可以访问外部函数(例如Board)中定义的变量和函数。该handleClick函数可以读取squares状态并调用setSquares方法,因为它们都是在Board函数内部定义的。

- 不可变性原则immutability:避免直接修改状态数据,创建一个状态的副本,修改这个副本,然后用新的副本来更新状态。在事件处理函数中,用浅拷贝创建数组副本,再更新数组。

        1.避免直接修改状态:如squares[i] = "X",React可能无法检测到状态已经变化,因为对象或数组的引用没有变化。

        2.确保组件重新渲染:通过使用如.slice()方法创建数组的副本,然后修改这个副本,可以保证数组的引用是全新的。这样当新数组通过setSquares(nextSquares)设置为新状态时,React 会检测到状态对象的引用变化,并触发组件的重新渲染。

        3.维护历史状态:需要管理历史状态或进行撤销和重做操作(time travel)的应用中,保持原始状态的不变是很重要的。通过对原始状态进行浅拷贝而非直接修改,可以确保原始状态不被篡改。

        4.简化数据的比较:

                (1)使用浅拷贝创建新对象或数组:当更新对象或数组时,不要直接修改它。应该创建一个副本,并在这个副本上做修改。

                (2)利用React.memoshouldComponentUpdate:

                对于函数组件,可以用React.memo来包裹组件,它会对组件的props进行浅比较,如果props没有变化,则不会重新渲染组件。

                对于类组件,可以通过实现shouldComponentUpdate方法来决定组件是否应该更新。在这个方法中,你可以通过比较新旧props或state来决定是否需要渲染。(也可以使用PureComponen--是React中的一个类组件,它自动实现了shouldComponentUpdate,通过浅比较props和state来避免不必要的渲染。)

-浅拷贝:数组中的元素(这里是字符串或null)被复制到一个新的数组对象中,但元素本身是基本类型,不涉及深拷贝的问题。修改这个新数组的某个元素并不会影响原始数组。

- Too many re-renders错误:传递了 handleClick(0) 作为 Square 组件的 onSquareClick prop 的值。 handleClick(0) 是一个函数调用,而不是一个函数引用。这意味着每当 Board 组件渲染时,handleClick(0) 都会被调用,导致状态更新并再次触发渲染。由于每次渲染都会触发另一个渲染,形成无限循环。

- 解决方法: 向 Square 组件传递一个函数,而不是函数调用的结果。这可以通过将 handleClick 函数包装在另一个函数中来实现

<Square value={squares[0]} onSquareClick={() => handleClick(0)} />

- 这样单击Square,子Square组件现在会要求父Board组件更新棋盘的状态。当Board的状态发生变化时,Board组件和每个子组件Square都会自动重新渲染。

-React命名惯例:

        使用onSomething的形式来命名表示事件的props。这里的“Something”通常是事件的名称,比如onClickonSubmit等。(对于内置的DOM元素,如<button>,其onClick属性具有特殊的意义,因为它直接关联到React的事件处理系统。这意味着当用户点击按钮时,React知道如何捕捉这个事件并执行相应的处理函数。)

        使用handleSomething的形式来命名处理这些事件的函数。这里的“Something”通常与事件相关联,例如handleClickhandleSubmit等。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值