【问题标题】:React: return object from hook causing object re-create on every render反应:从钩子返回对象,导致在每次渲染时重新创建对象
【发布时间】:2022-01-12 09:45:39
【问题描述】:

我尝试创建自定义挂钩来验证我的输入。但是如果自定义钩子返回一个对象,它会在页面上的每次更改时重新创建

const useInput = (initialValue) => {
    const [value, setValue] = useState(initialValue)
    const [state, setState] = useState('')

    return {value, state}
}

const user = useInput('')

这样我就可以使用

user.value
user.state

但如果页面上有多个输入,一个输入会触发所有其他输入重新创建

useEffect(() => {
    console.log('This triggers on every change on the page')
}, [user])

但如果我先这样破坏它,那一切都很好

const {userValue, userState} = useInput('')

useEffect(() => {
        console.log('Does not trigger on other inputs')
}, [userValue, userState])

或者,如果我使用 useMemo 返回对象,它也可以正常工作:

const useInput = (initialValue) => {
    const [value, setValue] = useState(initialValue)
    const [state, setState] = useState('')

    const obj = useMemo(() => {
        return {value, state}
    }, [value, state])

    return obj
}

const user = useInput('')

这是什么原因,为什么“用户”对象会发生变化,即使它没有变化,并且它在值上使用 useState。为什么在销毁或使用 useMemo 后停止重新创建?

我是 React 新手,并试图理解它。

【问题讨论】:

  • 感谢您很好地解释了这个问题。你能创建一个分钟吗?代码沙箱中的复制示例?想查看其他输入以及您如何使用钩子
  • 试试不要把user放到useEefect deps中,而是把你要检查的属性放在里面?
  • @NickParsons 谢谢!但是为什么破坏对象不会触发useEffect,即使没有useMemo()

标签: javascript reactjs react-hooks


【解决方案1】:

每次调用useInput 时,您都会使用对象文字语法{} 创建一个新对象,然后将其返回并存储在user 中。因为它是一个新对象,它是内存中的一个新引用,所以它不被认为等于最后一个 user 对象(因为对象是通过引用进行比较的)。因此,您的 useEffect() 回调触发。但是,当您使用useMemo() 时,如果valuestate 没有更改,它将返回相同的对象引用,因此如果valuestate 仍然相同,则不会创建新对象,而是重用旧对象。

解构之所以有效,是因为当您从对象中解构值时,您会将原始字符串值提取到变量中。由于字符串是基元,它们是按值比较的,不像对象是按引用比较的。由于依赖数组中解构字符串的值与上次渲染的值相同,因此您的 useEffect() 不会触发回调。

更多详细信息,请参见下面的代码 sn-p:

const foo = () => { // similar to your `useInput()` function
  // These behave like your state values
  const a = "a";
  const b = "b";
  
  return {a, b};
}

// --- Let's try and use objects ---
// First render
const obj1 = foo(); // on each render, you call `userInput()` 

// Second render
const obj2 = foo(); // `userInput()` called again, this creates a complelty new object in memory that is different to `obj1`
console.log("Is obj1 equal to obj2?", obj1 === obj2); // false - React compares the prev object reference to the current to decide if it should call the useEffect() callback, since they're not equal, react calls the callback

// --- Let's try and pull out the values ---
// First render
const {a, b} = foo(); // `a` and `b` are string values (primitives)

// Second render (using aliasing with destructuring to avoid naming conflicts)
const {a:a2, b:b2} = foo();
console.log("Is a and b from the first render equal to a2 and b2 from the second?:", a === a2 && b === b2); // true - Since a2 is equal to a from the first "render", and b is equal to b2, React doesn't call the callback

【讨论】:

  • 感谢您的解释!祝你有美好的一天!
  • @cogito 不用担心 :) 我添加了一个代码 sn-p,它还可以阐明如何将基元与对象进行比较
  • 谢谢!这很有帮助:)
猜你喜欢
  • 1970-01-01
  • 2021-04-23
  • 2020-06-12
  • 1970-01-01
  • 1970-01-01
  • 2021-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-04
相关资源
最近更新 更多