3

每个初级 React 开发人员都会犯的八个错误

 1 year ago
source link: https://www.51cto.com/article/717933.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

每个初级 React 开发人员都会犯的八个错误

作者:庄志炎 2022-09-04 15:28:25

每个学习 React 的程序员在学习过程中都会犯大量的错误。 有时他们甚至不知道自己犯了这些错误。 如果您精通 React,则需要避免这些错误并根据最佳实践进行编码。 所以,我想展示你们所做的错误并帮助纠正它们。 在此之前,我们需要知道我们要处理什么。

每个学习 React 的程序员在学习过程中都会犯大量的错误。 有时他们甚至不知道自己犯了这些错误。 如果您精通 React,则需要避免这些错误并根据最佳实践进行编码。 所以,我想展示你们所做的错误并帮助纠正它们。 在此之前,我们需要知道我们要处理什么。

什么是 React JS?

React 是 Facebook 于 2011 年开发的一个 javascript 库。大多数人将其误解为一个框架。 这是一个非常基础的组件库。 它允许开发人员为网站和应用程序构建快速的用户界面。 让我们看看我们使用 React 的一些关键原因。

  1. 虚拟 DOM 增强了高性能。
  2. 组件的可重用性。
  3. 庞大的社区。
  4. Flux 和 Redux 的强大功能。
  5. 由于它使用 JSX,任何了解 HTML 的人都可以轻松学习它。
  6. 独特的反应钩子

然后,让我们谈谈大多数开发人员犯的错误以及如何纠正这些错误。

与更新状态相关的错误

我要告诉你们两个你们犯错误的场景。 认为您的组件中有一个名为 number 的状态。

const [number, setNumber] = useState(0);

现在,您将更新该状态并在下一行中访问它。

setNumber(5);
console.log(number);

你会认为这会安慰 5。但事实并非如此。 它将先前的值控制台为 0 。

你应该知道setState函数(这里是setNumber)是一个异步函数。 由于该功能是异步控制器在更新之前不会保持。 它将转到下一行并控制 number 的值(它将控制 0,因为该数字尚未更新 5)。 因此,如果您正在使用 setNumner 语句下方的 number 的更新值编写另一个语句,那么您的逻辑将完全错误。

在类组件中,我们有一种方法可以在更新后立即访问更新的值。 但是对于功能组件,我们不能使用这种方法。

您可以使用 setState 调用回调函数来访问更新的值。

this.setState({ number: 5 }, () => console.log(this.state.number));

现在,它将控制台 5 。 这是完整的代码段。

import React, { Component } from "react";

export default class TestComponent extends Component {
  constructor() {
    super();
    this.state = { number: 0 };
  }

  handleClick = () => {
    this.setState({ number: 5 }, () => console.log(this.state.number));
  };

  render() {
    return (
      <>
        <h2>Name : {this.state.number}</h2>
        <button onClick={this.handleClick}>Click</button>
      </>
    );
  }
}

让我们看看你在更新状态时犯的另一个错误。 想想这样的场景。 你有一个名为 number 的变量和两个按钮来增加这个变量的值。 一个按钮是普通按钮,数字加1。另一个按钮是异步增加数字。 该按钮发生错误。 这是大多数开发人员编写的场景的代码。

import { useState } from "react";

function App() {
  const [number, setNumber] = useState(0);

  const handleClick = () => {
      setNumber(number + 1);
  };

  const handleClickAsync = () => {
    setTimeout(()=> {
      setNumber(number + 1);
    }, 4000)
  };
  return (
    <>
      <h2>Number : {number}</h2>
      <button onClick={handleClick}>Increase</button>
      <button onClick={handleClickAsync}>IncreaseAsync</button>

    </>
  );
}

export default App;

增加按钮没有任何问题。 但是如果我们点击一次IncreaseAsync按钮,然后点击Increase Button 5次,在4秒之前数字会增加到5。但是在4秒之后数字会是1。原因是在handleClickAsync函数中,当数字更新时,setNumber函数获取 单击“IncreaseAsync”按钮时的数字值。 那一刻,数字的值为0。所以,4秒后数字变成了1。

我们可以像这样设置 setNumber 函数来克服这个问题。 然后,它将获取要更新的数字的当前值。

setNumber((prevSate)=> prevSate + 1);

更正后完整的代码将是这样的。

import { useState } from "react";

function App() {
  const [number, setNumber] = useState(0);

  const handleClick = () => {
    setNumber((prevSate) => prevSate + 1);
  };

  const handleClickAsync = () => {
    setTimeout(() => {
      setNumber((prevSate) => prevSate + 1);
    }, 4000);
  };
  return (
    <>
      <h2>Number : {number}</h2>
      <button onClick={handleClick}>Increase</button>
      <button onClick={handleClickAsync}>IncreaseAsync</button>
    </>
  );
}

export default App;

如果需要,您可以将 setNumber 放在 handleClick 中。 那不会有什么不同。

如何正确初始化状态 [对于对象]

这也是大多数初级开发人员常犯的错误。 他们初始化一个没有默认值的状态。 对于单个整数或字符串,这不是什么大问题。 但是如果你正在初始化一个对象,你必须提到默认值。 否则,访问该对象的属性时会出错。 让我们获取一个名为 user 的对象。

如果你像这样初始化它。

const [user, setUser] = useState();

之后在其他地方为它分配一个值。

setUser({
  name: "kushan",
  age: 24,
  friends: ["john", "Anne", "will"]
});

之后,如果您像这样访问对象属性,您将收到错误消息。

<h2>name : {user.name}</h2>

错误会这样说:

772ef5517deec2be6691581b3518036ade669f.jpg

您可以进行两项更改来进行此写入。

1.访问前添加&&

<h2>name : {user && user.name}</h2>

2. 添加?。 访问时

<h2>name : {user?.name}</h2>

但是,我建议您在初始化时添加一个默认值。 您可以添加具有空值的预期属性。

const [user, setUser] = useState({
   name: "",
   age: "",
   friends: [],
});

在这里,我们将讨论如何以适当的方式更新对象。 让我们使用具有初始值的前一个用户对象。

const [user, setUser] = useState({
   name: "kushan",
   age: 24,
   friends: ["john", "Anne", "will"],
});

添加带有按钮的输入字段以更改用户对象的名称。 我已经分别显示了对象属性。

import { useState } from "react";

function App() {
  const [user, setUser] = useState({
    name: "kushan",
    age: 24,
    friends: ["john", "Anne", "will"],
  });
  const [input, setInput] = useState("");
  const handleClick = () => {
    //
    //
    // update the object here
    //
    //
  };

  return (
    <>
      <h2>name : {user.name}</h2>
      <h2>age : {user.age}</h2>
      <h2>friends :</h2>
      <ul>
        {user.friends?.map((friend) => (
          <li key={friend}>
            <h4>{friend}</h4>
          </li>
        ))}
      </ul>

      <input
        type="text"
        placeholder="Enter new name"
        onChange={(e) => setInput(e.target.value)}
      />
      <button onClick={handleClick}>Click</button>
    </>
  );
}

export default App;
35cedca75562d2cd05934009558491bb381228.jpg

首先,我将展示开发人员执行此操作的一些错误方式。

setUser(input);
or
setUser({name : input});

这将替换对象并分配我们在此处输入的输入。 那不是我们想要的。 这就是它正确更新的方式。

setUser({...user, name : input});
or
setUser((prevState) => ({ ...prevState, name: input }));

创建一个对象而不是单独的状态

在每个 Web 应用程序中,我们都使用对象。 一些开发人员为对象的属性定义单独的状态。 这会花费大量时间并降低代码的可读性。 想想,我们的应用程序中有一个用户,我们创建了一个表单来获取用户详细信息。 你打算用什么来存储这些用户详细信息?

在这样的不同状态下:

const [fName, setFName] = useState("");
const [lName, setLName] = useState("");
const [email, setEmail] = useState("");
const [age, aetAge] = useState("");
const [city, setCity] = useState("");
const [gender, setGender] = useState("");
const [password, setPassword] = useState("");

不……您应该使用一种状态来创建它:

const [user, setUser] = useState({
    fName: "",
    lName: "",
    email: "",
    age: "",
    city: "",
    gender: "",
    address: "",
    password: "",
});

为表单中的所有输入创建单个 onChage 函数

让我们以上面的场景为例。 我们可以创建一个表单来获取该用户的详细信息。 在您将如何为这些输入字段创建 onChange 函数之后。

<input type='text' name="fName" />
<input type='text' name="lName" />
<input type='text' name="email" />
<input type='text' name="age" />
<input type='text' name="city" />
<input type='text' name="gender" />
<input type='text' name="address" />
<input type='password' name="password" />

你要创建单独的 onChange 函数吗?

onChange={(e) => setUser((prevState)=>({ ...prevState, fName: e.target.value }))}onChange={(e) => setUser((prevState)=>({ ...prevState, lName: e.target.value }))}onChange={(e) => setUser((prevState)=>({ ...prevState, email: e.target.value }))}....likewise

情况不妙。 您可以使用更好的方法来执行此操作。 为所有输入字段创建一个 onChange 函数。 您需要将用户的相同属性名称添加到输入标签的名称属性中。

const handleChange = (e) => {
setUser((prevState) => ({...prevState,[e.target.name]:e.target.value  }));
};

这里的完整代码:

import { useState } from "react";

function App() {
  const [user, setUser] = useState({
    fName: "",
    lName: "",
    email: "",
    age: "",
    city: "",
    gender: "",
    address: "",
    password: "",
  });

  const handleChange = (e) => {
    setUser((prevState) => ({ ...prevState, [e.target.name]: e.target.value }));

  };

  return (
    <div>
      <form>
        <input type="text" onChange={handleChange} name="fName" placeholder="fName" />
        <input type="text" onChange={handleChange} name="lName" placeholder="lName" />
        <input type="text" onChange={handleChange} name="email" placeholder="email" />
        <input type="text" onChange={handleChange} name="age" placeholder="age" />
        <input type="text" onChange={handleChange} name="city" placeholder="city" />
        <input type="text" onChange={handleChange} name="gender" placeholder="gender" />
        <input type="text" onChange={handleChange} name="address" placeholder="address" />
        <input type="password" onChange={handleChange} name="password" placeholder="password" />
      </form>
      <button >Submit</button>
    </div>
  );
}

export default App;

直接 DOM 操作

你以前写过这样的代码吗:

function App() {
   const handleClick = () => {
       const menu = document.querySelector(".menu");
       menu.classList.add("color");
   }
   return (
        <>
        <div className="menu">Sample Text</div>
        <button onClick={handleClick}>Click</button>
        </>
   );
}

这在新开发人员中很常见。 我们不应该在反应中直接进行 DOM 操作。 为此,我们可以在我们的 react 组件中使用状态,而不是访问 DOM 元素并向它们添加类。 如果应用程序混淆了 DOM 内部的状态,它将变得更难测试、更难调试并且更难推理。

在这里,一个可能的解决方案。 我在没有使用 document.querySelector 的情况下完成了任务。

import { useState } from 'react';
import './App.css'function App() {
    const [isColored, setIsColored] = useState(false);
    const handleClick = () => {
         setIsColored(true);
    }
    return (
       <>
       <div className={isColored ? "color": "" + "menu" }>Sample     Text</div>
       <button onClick={handleClick}>Click</button>
       </>
    );
}

useState 与 useReducer(何时使用?)

当我们需要一个状态时,在任何地方都使用 useState 是不是一个常量? 答案是不。 有时我们有更好的选择,而不是使用 useSate。 它是 useReducer 。 当然,没有规定你应该使用 useReducer 而不是 useState,如果你仍然使用它也没有错。 但在某些特定情况下,useReducer 更有优势。

例如,假设您有一个更复杂的对象,它具有不同的属性,如数组和嵌套对象。

const [post, setPost] = useState({
   title: "",
   description: "",
   date: "",
   category: "",
   image: [],
   tag: [],
   owner: {
      name: "",
      email: "",
   }
});

然后,最好使用 reducer,因为当你更新不同的属性时,这里会很乱。 因为这里的所有元素都不是前面示例中的字符串或数字。 所以我们不能在一个函数中更新它们。 因此,在这里创建一个 useReducer 并为每个属性使用不同的操作可能会更有用。 因为最好使用 useReducer 。

小心 React 派生的状态

为了理解这一点,我创建了一个小场景。 假设我们有一个名为 classRooms 的状态,它是一个对象数组。

const [classRooms, setClassRooms] = useState([
   { id: 1, className: "Sons of Sun", studentCount: 12 },
   { id: 2, className: "Blooming Volcanoes", studentCount: 10 },
   { id: 3, className: "Pillaging Pirates", studentCount: 20 },
   { id: 4, className: "Ping Bombs", studentCount: 8 },
]);

我们要做的是,我们将在屏幕上显示这个数组,并给出一个选择按钮和一个增加按钮(用于增加学生)。 所选的班级也将显示出来。

326702a66b2fea4fc5221358e321a6e836214c.png

这是代码。 但我做错了。

import { useState } from "react";
import "./App.css";

function App() {
  const [classRooms, setClassRooms] = useState([
    { id: 1, className: "Sons of Sun", studentCount: 12 },
    { id: 2, className: "Blooming Volcanoes", studentCount: 10 },
    { id: 3, className: "Pillaging Pirates", studentCount: 20 },
    { id: 4, className: "Ping Bombs", studentCount: 8 },
  ]);

  const [selectedClass, setSelectedClass] = useState(null);

  const handleSelect = (id) => {
    setSelectedClass(classRooms.find((classRoom) => classRoom.id === id));
  };
  const handleIncrease = (id) => {
    setClassRooms((prevState) => {
      return prevState.map((classRoom) => {
        if (classRoom.id === id) {
          return { ...classRoom, studentCount: classRoom.studentCount + 1 };
        } else return classRoom;
      });
    });
  };

  return (
    <div className="container">
      <div className="classRooms">
        {classRooms.map((classRoom) => (
          <div className="classRoom">
            <div className="text">
              <h4>{classRoom.className}</h4>
              <h2>{classRoom.studentCount}</h2>
            </div>
            <button onClick={() => handleSelect(classRoom.id)}>Select</button>
            <button onClick={() => handleIncrease(classRoom.id)}>
              Increase Student
            </button>
          </div>
        ))}
      </div>

      <div className="selectedRoom">
        <div className="classRoom">
          <h4>Class Name : {selectedClass?.className}</h4>
          <h2> Student No : {selectedClass?.studentCount}</h2>
        </div>
      </div>
    </div>
  );
}

export default App;

这将增加学生人数并按预期选择相关课程。 但是如果你在选择教室后尝试增加学生人数,学生人数就会增加。 但所选区域的学生人数不会更新。 我们实现代码的方式就是原因。 大多数开发人员都会犯这个错误。 我们将如何解决这个问题?

您可以将所选教室的唯一ID存储在该州中。 然后取一个名为 selectedClassID 的变量并将选定的教室分配给它。

const [selectedClass, setSelectedClass] = useState(null);// not thisconst [selectedClassID, setSelectedClassID] = useState(null);//do like this

声明并赋值给 selectedClassID

const selectedClass = classRooms.find((classRoom) => classRoom.id === selectedClassID);

接下来,改变handleSelect函数

const handleSelect = (id) => {
    setSelectedClassID(id);
};

这是修正后的完整代码:

import { useState } from "react";
import "./App.css";

function App() {
  const [classRooms, setClassRooms] = useState([
    { id: 1, className: "Sons of Sun", studentCount: 12 },
    { id: 2, className: "Blooming Volcanoes", studentCount: 10 },
    { id: 3, className: "Pillaging Pirates", studentCount: 20 },
    { id: 4, className: "Ping Bombs", studentCount: 8 },
  ]);

  const [selectedClassID, setSelectedClassID] = useState(null);
  const selectedClass = classRooms.find(
    (classRoom) => classRoom.id === selectedClassID
  );

  const handleSelect = (id) => {
    setSelectedClassID(id);
  };
  const handleIncrease = (id) => {
    setClassRooms((prevState) => {
      return prevState.map((classRoom) => {
        if (classRoom.id === id) {
          return { ...classRoom, studentCount: classRoom.studentCount + 1 };
        } else return classRoom;
      });
    });
  };

  return (
    <div className="container">
      <div className="classRooms">
        {classRooms.map((classRoom) => (
          <div className="classRoom">
            <div className="text">
              <h4>{classRoom.className}</h4>
              <h2>{classRoom.studentCount}</h2>
            </div>
            <button onClick={() => handleSelect(classRoom.id)}>Select</button>
            <button onClick={() => handleIncrease(classRoom.id)}>
              Increase Student
            </button>
          </div>
        ))}
      </div>

      <div className="selectedRoom">
        <div className="classRoom">
          <h4>Class Name : {selectedClass?.className}</h4>
          <h2> Student No : {selectedClass?.studentCount}</h2>
        </div>
      </div>
    </div>
  );
}

export default App;

所以,本文到此结束。 我已经讨论了8个会出错的点。 大多数初学者开发人员一直在做这些错误的方式。 因此,我认为这将帮助您纠正错误并更上一层楼。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK