关于React 6个你需要注意的地方

1,勿滥用 useState,非受控组件可以直接通过 ref 从 dom 上取值

import React, { useState } from "react"

type Props = {}

export default function Form({}: Props) {
    const [email, setEmail] = useState("")
    const [password, setPassword] = useState("")
    function onSubmit(e) {
        e.preventDefault()
        console.log({ email, password })
    }
    return (
        <form onSubmit={onSubmit}>
            <label htmlFor="email">Email</label>
            <input
                type="email"
                id="email"
                onChange={e => setEmail(e.target.value)}
            />
            <label htmlFor="password">Password</label>
            <input
                type="password"
                id="password"
                onChange={e => setPassword(e.target.value)}
            />
            <button type="submit">Login</button>
        </form>
    )
}

这个代码看着似乎没有任何问题,运行起来也很好。可是我们会发现这个email,password只在提交表单的时候会使用,而每次我们进行输入改变这个状态变量的时候都会让组件重新渲染,这并不好。所以下面这个版本,使用 ref 能很好解决这个问题。

import React, { useRef } from "react"

export default function Form() {
    const emailRef = useRef()
    const passwordRef = useRef()
    function onSubmit(e) {
        e.preventDefault()
        console.log({
            email: emailRef.current.value,
            password: passwordRef.current.value,
        })
    }
    return (
        <form onSubmit={onSubmit}>
            <label htmlFor="email">Email</label>
            <input ref={emailRef} type="email" id="email" />
            <label htmlFor="password">Password</label>
            <input ref={passwordRef} type="password" id="password" />
            <button type="submit">Login</button>
        </form>
    )
}

这种写法不仅更加简洁优雅而且可以避免在输入的时候组件重新渲染。我们在写表单的组件的时候可以尝试用useRef替代useState。

2,关于useState 

import React, { useState } from "react"

export default function Count() {
    const [count, setCount] =useState (0)
    function fn(num){
        setCount(count+num)
        setCount(count+num)
    }
    return (
        <>
            <button onClick={()=>{fn(-1)}}>-</button>
            {count}
            <button onClick={()=>{fn(+1)}}>+</button>
        </>
    )
}

会发现计数器函数fn虽然执行了两次setCount但是还是一次只加1,这是因为React的状态更新是异步的,所以两次setCount调用会在同一渲染周期中处理。每次setCount的时候都是取一开始count的状态。

    function fn(num) {
        setCount(currentCount => {
            return currentCount + num
        })
        setCount(currentCount => {
            return currentCount + num
        })
    }

而如果这么写的话,就会发现一次点击会加2。这是因为这种写法使用了函数形式的setCount,函数接受了currentCount作为参数,确保了在每次更新状态时都是基于最新的状态。因此,这两次更新将各自增加num的值到状态中。

3,useState的异步解决方案

    function fn(num) {
        setCount(currentCount => {
            return currentCount + num
        })
        console.log(num);
    }

由于setState是一个异步函数,所以在使用后直接使用state状态的时候,并不会取最新值。我们可以使用useEffect监听state的变化,代码也很简单。

    function fn(num) {
        setCount(currentCount => {
            return currentCount + num
        })
    }
    useEffect(() => {
        console.log(count)
    }, [count])

4, 请勿滥用useEffect

import React, { useEffect, useState } from "react"

export default function Count() {
    const [firstName, setFirstName] = useState("")
    const [lastName, setLastName] = useState("")
    const [fullName, setFullName] = useState("")
    useEffect(() => {
        setFullName(`${firstName} ${lastName}`)
    }, [firstName, lastName])
    return (
        <>
            <input
                type="text"
                onChange={e => {
                    setFirstName(e.target.value)
                }}
            />
            <br />
            <input
                type="text"
                onChange={e => {
                    setLastName(e.target.value)
                }}
            />
            <br />
            {fullName}
        </>
    )
}

这样写的话,也是能正常工作运行。但是会发现,用useEffect监听以及在使用setFullName的时候多少有些冗余以及同样的重复渲染。

    const [firstName, setFirstName] = useState("")
    const [lastName, setLastName] = useState("")
    const fullName = `${firstName} ${lastName}`

什么时候用useState,什么时候直接用const,值得我们去实践与思考。

5,useMemo实用小技巧

import React, { useEffect, useState } from "react"

export default function Count() {
    const [lastName, setLastName] = useState("")
    const [firstName, setFirstName] = useState("")
    const [num, setNum] = useState(0)
    const fullName = {
        lastName,
        firstName,
    }
    useEffect(() => {
        console.log(fullName)
    }, [fullName])
    return (
        <>
            <input
                type="text"
                onChange={e => {
                    setLastName(e.target.value)
                }}
            />
            <br />
            <input
                type="text"
                onChange={e => {
                    setFirstName(e.target.value)
                }}
            />
            <br />
            <button onClick={() => setNum(num + 1)}>click</button>
            {num}
        </>
    )
}

大家可以复制代码到本地实践一下。我们会很神奇的发现,我们不仅在输入框中输入的时候会输出fullName,在点击button按钮的时候也会输出fullName!为什么呢? 原来,这是因为每次setNum的时候,react会重新渲染组件,所以会从上到下读一遍代码,当读到

 const fullName = {
        lastName,
        firstName,
    }

的时候。react会对fullName重新赋值。也就是说fullName会改变(虽然看起来对象似乎没有改变,但我们知道  {} === {} 是一个false ,所以重新赋值之后就是不一样的对象了)。然后就会执行useEffect函数。

怎么避免这种情况呢,我们可以使用useMemo。

    const fullName = useMemo(() => {
        return {
            lastName,
            firstName,
        }
    }, [lastName, firstName])

把fullName这个对象用useMemo写,并监听键,就可以不会反复的去让fullName去重新赋值了。可能现在看来,有些例子的钩子用法看着跟原来的写法也没啥区别,可是等工作的时候,就会发现这些基础知识知道了就是知道了,并且会在某个瞬间让你恍然大悟。笔者就深有体会,在某一天改bug的时候,看到同事写的代码中用了useEffect监听一个字段,当你要对这个字段进行维护的时候,就会发现很容易出问题,但是如果你对useEffect的各种用法烂熟于心,就会在维护的过程中得心应手很多。还有的时候看到useMemo的时候也不会想为什么要在这用,不用的话会产生什么影响之类的疑惑了。还有就是呢,面试也爱问useMemo,useCallback这些,到时候面试官一问useMemo你把我这个例子给他这么一说,也能对你印象好不少。

6,一个实用的自定义钩子

最后是一个自定义钩子,也是面试官爱问的东西。笔者在面试小米的时候就有被问到平时是否有自己写过自定义钩子,返回值是什么之类的。

import { useEffect, useState } from "react"

const useFetch = url => {
    const [loading, setLoading] = useState(true)
    const [data, setData] = useState()
    const [error, setError] = useState()
    useEffect(() => {
        const controller = new AbortController()
        setLoading(true)
        fetch(url, { signal: controller.signal })
            .then(setData)
            .catch(setError)
            .finally(() => setLoading(false))
        return () => {
            controller.abort()
        }
    }, [url])
    return { loading, data, error }
}

export default useFetch

亮点在于当url输入时候导致频繁改变的时候,会触发  controller.abort() 清理副作用,取消请求,也就是只有最后一次请求被完全处理。 例如在处理实时搜索时,你可能只关心用户最后一次输入的搜索词。然而,在其他情况下,你可能想要在useEffect的依赖数组中包含更多的变量,以确保每次这些变量发生变化时都触发请求。controller.abort() 的作用是确保在组件卸载时取消正在进行的Fetch请求,以避免潜在的问题和资源泄漏,是一种处理异步操作的良好实践。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用React中的state和事件处理程序来实现这个功能。以下是一个简单的示例代码: ```jsx import React, { useState } from 'react'; function App() { const [showDiv, setShowDiv] = useState(false); const handleClick = () => { setShowDiv(true); } return ( <div> <button onClick={handleClick}>添加div</button> {showDiv && <div>这是一个新的div</div>} </div> ); } export default App; ``` 在这个例子中,我们使用useState来创建一个名为showDiv的状态变量,并将其默认值设置为false。当用户点击按钮时,我们调用handleClick函数来更新showDiv的值为true。然后,我们使用showDiv的值来决定是否渲染一个新的div元素。如果showDiv为true,那么我们将显示一个新的div元素。 ### 回答2: 在 React 中,要实现点击按钮添加一个 `<div>` 的功能,可以按照以下步骤进行: 首先,在组件的 `state` 中设置一个变量,用于表示是否需要显示这个 `<div>`。例如,将其命名为 `showDiv`,默认值设为 `false`。 然后,在组件的 `render` 方法中,根据 `showDiv` 的值,决定是否渲染这个 `<div>`。可以使用条件渲染的方式,在 `<div>` 的外部使用 `if` 语句或三元运算符进行判断。 接下来,处理点击按钮的事件。在组件中定义一个函数,例如 `handleButtonClick`,用于处理按钮点击事件。在这个函数中,通过调用 `setState` 方法来更新 `showDiv` 的值为 `true`。 最后,在 JSX 代码中,使用 `<button>` 元素作为按钮,并将 `handleButtonClick` 函数作为其 `onClick` 属性的值。这样,当点击按钮时,`handleButtonClick` 函数会被调用,从而更新 `showDiv` 的值为 `true`,触发重新渲染,进而显示这个 `<div>`。 结合以上步骤,就能够实现点击按钮添加一个 `<div>` 的功能了。在这个过程中,通过改变 `state` 的值,从而触发组件的重新渲染,达到动态添加 `<div>` 的效果。 ### 回答3: React是一个用于构建用户界面的JavaScript库。要实现点击按钮添加一个div的功能,需要先创建一个React组件,并在组件的state中保存一个用于判断是否显示div的变量。然后在按钮的点击事件中修改这个变量的值,达到添加div的效果。 首先,在React中创建一个叫做AddDivButton的组件,并在组件的构造函数中初始化state。代码如下: ```javascript import React from 'react'; class AddDivButton extends React.Component { constructor(props) { super(props); this.state = { showDiv: false }; } handleClick = () => { this.setState({ showDiv: !this.state.showDiv }); } render() { return ( <div> <button onClick={this.handleClick}>点击按钮</button> {this.state.showDiv && <div>要添加的div</div>} </div> ); } } export default AddDivButton; ``` 在上面的代码中,通过`setState`方法改变`showDiv`变量的值,当`showDiv`为`true`时渲染要添加的div。 然后,在需要使用这个组件的地方,将它引入并渲染到页面上,如下所示: ```javascript import React from 'react'; import AddDivButton from './AddDivButton'; class App extends React.Component { render() { return ( <div> <AddDivButton /> </div> ); } } export default App; ``` 通过以上代码,当用户点击按钮时,就会添加一个div到页面中。 注意,以上只是一个简单的示例,实际开发中可能会有更多的逻辑和样式处理。希望能帮到你!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值