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


(相关资料图)

什么是 React JS?

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

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

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

与更新状态相关的错误

我要告诉你们两个你们犯错误的场景。 认为您的组件中有一个名为 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 (      <>        

Name : {this.state.number}

); }}

让我们看看你在更新状态时犯的另一个错误。 想想这样的场景。 你有一个名为 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 (    <>      

Number : {number}

);}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 (    <>      

Number : {number}

);}export default App;

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

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

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

如果你像这样初始化它。

const [user, setUser] = useState();

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

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

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

name : {user.name}

错误会这样说:

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

1.访问前添加&&

name : {user && user.name}

2. 添加?。 访问时

name : {user?.name}

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

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 (    <>      

name : {user.name}

age : {user.age}

friends :

    {user.friends?.map((friend) => (
  • {friend}

  • ))}
setInput(e.target.value)} /> );}export default App;

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

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

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

setUser({...user, name : input});orsetUser((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 函数之后。

你要创建单独的 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 (    
);}export default App;
直接 DOM 操作

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

function App() {   const handleClick = () => {       const menu = document.querySelector(".menu");       menu.classList.add("color");   }   return (        <>        
Sample Text
);}

问题?

这在新开发人员中很常见。 我们不应该在反应中直接进行 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 (       <>       
Sample Text
);}
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 },]);

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

这是代码。 但我做错了。

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 (    
{classRooms.map((classRoom) => (

{classRoom.className}

{classRoom.studentCount}

))}

Class Name : {selectedClass?.className}

Student No : {selectedClass?.studentCount}

);}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 (    
{classRooms.map((classRoom) => (

{classRoom.className}

{classRoom.studentCount}

))}

Class Name : {selectedClass?.className}

Student No : {selectedClass?.studentCount}

);}export default App;

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

推荐内容