【问题标题】:Why Does Electron React Passing Prop Uses Older State?为什么 Electron React 传递 Prop 使用旧状态?
【发布时间】:2021-11-29 01:52:05
【问题描述】:

所以我刚接触 Electron 和 React。我想实现更多功能,但遇到了一个问题,当我将函数 c 传递给新的 Card 元素时,它似乎在创建时传递了函数 c 中的钩子。

这样: 当 + 按钮被点击 3 次时,它将按预期呈现 3 张卡片,让我们称它们为 0、1 和 2。 但是,当他们的日志按钮被点击控制台记录卡钩时,点击 0 的日志按钮控制台 日志:

{}

点击1的日志按钮控制台日志:

{0: {react element of 0}}

点击2的日志按钮控制台日志:

{0: {react element of 0}, 1: {react element of 1}}

但是,单击我目前在 App.js 中设置的用于解决此问题的 - 按钮会产生:

{0: {react element of 0}, 1: {react element of 1}, 2: {react element of 2}}

我是否错误地认为单击每个日志按钮应该改为控制台记录 App.js 控制台中的 - 按钮记录的内容。而不是创建卡片元素时钩子的样子?我该如何解决这个问题?

注意:我使用 Object 而不是 Array 来存储 React 元素的原因是因为我想最终添加一个删除按钮来删除单个卡片。有没有更好的方法来做我正在做的事情?

App.js

import './App.css';
import Cards from './Cards/Cards'
import { useState } from 'react';

let KEY = 0

export default function App() {
  const [cards, setCards] = useState({})

  const c = () => {
    console.log(cards)
  }
  const make = () =>{
    const a = <Cards log={c} anID={KEY}/>
    const b = {...cards}
    b[KEY] = a
    KEY += 1
    setCards(b)
  }
  return (
    <div>
      <div className="time-container">
        {Object.values(cards)}
      </div>
      <button className="add-button" onClick={() => make()} >+</button>
      <button className="" onClick={() => console.log(cards)} >-</button>
    </div>
  );

卡片.js

import React from 'react'
import './Cards.css'

export default function Cards({ log, anID }) {
    return <div className="card-container">

        <div>

            <button className="card-buttons-red" onClick={()=> log(anID)}>Log</button>
            <button className="card-buttons">Edit</button>

        </div>
    </div>
}

【问题讨论】:

  • 对于卡片,您应该使用数组而不是对象。
  • 您正在使用已创建的对象在您的应用程序中查看。因此,c 函数签名不会根据cards 的更改而更改。为此,您需要在组件内 map 您的 Cards 组件。
  • @Invizi 我的想法是将卡片存储在一个对象中,以便我可以通过它们的键识别它们,以便稍后添加删除功能
  • @KavinduVIndika 我认为c应该指向cards钩子,它应该在一个特定的指针地址。但我想不会。我将如何映射Cards 组件?你的意思是我应该遍历对象并重建元素吗?因为那不会重新渲染旧卡,如果我向它们添加倒数计时器或其他东西,它可能会搞砸吗?对不起,如果你能给我一个很好的例子!
  • 正如@Invizi 提到的,您是创建数组还是对象并不重要。您应该创建jsx elements,以便在运行时显示在您的应用程序中。

标签: javascript reactjs react-hooks electron


【解决方案1】:

一旦您使用make() 函数创建了&lt;Cards log={c} anID={KEY} /&gt; jsx 元素,它就不会再被更改。 c 的函数签名不会根据 cards 状态的变化而改变,当您作为道具插入特定的 &lt;Cards/&gt; 组件时。这只是意味着,一旦您创建了&lt;Cards/&gt; 组件,函数参数或其输出就不会改变。

这就是为什么第一个 Log 按钮只记录{} 和第二个Log 按钮记录{0: {react element of 0}}

在这里您可以观察到您传递给子组件的c函数的函数签名在创建后没有更改过一次。

简单解释一下。

之前,第一次执行make()函数,cards对象是{}。 那么你可以理解当你先执行make()函数时,传递的c函数会如下。

  const c = () => {
    console.log({})
  }

这不会随着cards的变化而改变。

第二次执行make()时,传递的c函数如下。

  const c = () => {
    console.log({0: {react element of 0}})
  }

我不认为你真的需要在&lt;Cards/&gt; 组件中记录cards 对象。只要使用useEffect 更改cards 状态,只需将其记录在父组件中,如下所示。

let KEY = 0;

export default function App() {
  const [cards, setCards] = useState({});

  const c = () => {
    console.log(cards);
  };
  const make = () => {
    const a = <Cards log={c} anID={KEY} />;
    const b = { ...cards };
    b[KEY] = a;
    KEY += 1;
    setCards(b);
  };

  useEffect(() => {
    console.log("cards: ", cards);
  }, [cards]);

  return (
    <div>
      <div className="time-container">{Object.values(cards)}</div>
      <button className="add-button" onClick={() => make()}>
        +
      </button>
      <button className="" onClick={() => console.log(cards)}>
        -
      </button>
    </div>
  );
}

【讨论】:

  • 感谢您的解释并为我澄清了这一点,但是,如果您将其放入 useEffect 中,我认为它会改变我想要对 c 执行的操作的意图,即控制台记录钩子来自每张卡片中的 onClick。那么这是否意味着唯一的方法是在将新卡添加到挂钩时重建每张卡?
  • 是的,当你执行make() 函数时,应该有一种方法可以重新渲染以前创建的Cards 组件。否则传递的c函数签名不会改变。
【解决方案2】:

React 的工作方式是它在重新渲染(甚至更频繁)时重新运行您的组件函数。

然后重新创建组件函数体中的任何内容,除了钩子可能保留一些以前的信息。

在您的情况下,这意味着您的 c 函数和您的 cards 状态变量每次都是不同的引用。

然后,正如@KavinduVIndika 的回答所暗示的那样,在您的 make 函数中创建您的子 Cards 组件并将它们存储在您的 cards 状态时,您基本上嵌入这些引用。在下一次重新渲染时,会创建新的引用,从而导致您的症状。

一个直接的解决方法是尝试引用通过重新渲染“持久”但没有嵌入的东西。这就是useRef hook 的用例。

function App() {
  const [cards, setCards] = useState({})

  const c = useRef()

  c.current = () => {
    console.log(cards)
  }
  const make = () =>{
    const a = <Cards log={c} anID={KEY}/>
    const b = {...cards}
    b[KEY] = a
    KEY += 1
    setCards(b)
  }

  return <>(template)</>
}

// in Cards, call c.current
<button className="card-buttons-red" onClick={()=> log.current(anID)}>Log</button>

但正如@KavinduVIndika 的评论所暗示的那样,只有一种更常见的 React 方法可以完全避免此类问题。在我看来,这可能是由于您尝试过早优化,以预测您的计时器使用情况,但不知道 React 已经涵盖了最不利的性能瓶颈,即浏览器重绘,这要归功于它虚拟 DOM。是的,您的子 Cards 组件可能会重新运行(但通过正确使用 ref 特殊道具保留其状态),但在绝大多数情况下,您几乎看不到性能影响。

function App() {
  const [cards, setCards] = useState({})

  const c = () => {
    console.log(cards)
  }
  const make = () =>{
    const a = KEY // use plain data, do not store JSX
    const b = {...cards}
    b[KEY] = a
    KEY += 1
    setCards(b)
  }

  return (<>
    {Object.values(cards).map(cardData =>
      // by building the Cards within the App at render time,
      // it now uses the most up-to-date reference of your c function,
      // hence avoids past reference issue.
      // Performance-wise, React detects that the actual DOM
      // does not need visual update, hence no repaint.
      <Cards log={c} anID={cardData} ref={cardData} />
    )}
  </>)
}

【讨论】:

    猜你喜欢
    • 2021-05-11
    • 1970-01-01
    • 1970-01-01
    • 2017-11-05
    • 2018-02-13
    • 2023-03-05
    • 2018-01-29
    • 1970-01-01
    • 2016-10-19
    相关资源
    最近更新 更多