React、console.log 和 render 显示不同的值

Posted

技术标签:

【中文标题】React、console.log 和 render 显示不同的值【英文标题】:React, console.log and render display different values 【发布时间】:2021-02-07 18:10:21 【问题描述】:

似乎记录到控制台的对象在HTML上显示的内容不匹配,我对下面这个简单的例子感到困惑。这里的按钮切换了数组的顺序,React 似乎渲染了之前的列表。

=== 更新 3 ====

cuprite 是对象同情,setState(list=>list.push(12)) 将改变列表但不会触发 setState,因为列表的 id 仍然相同。

如果您将 State Hook 更新为与当前状态相同的值,React 将退出而不渲染子级或触发效果。 (React 使用 Object.is 比较算法。)React useState doc

=== 更新 2 ====

我想我找到了解决方案。罪魁祸首似乎是对象的可变性,解决方法是在Solution Sandbox 中创建排序列表的深层副本。我相信我发现了 React 的未定义行为,因为 list 的可变性不应该导致 console.log(list)<h1>list</h1> 的任何差异,如下图所示。 Correct modification of state arrays in ReactJS

=== 更新 1 ====

Sandbox 包含与下图相同的代码。

我的排序功能没有问题!如果升序,那么较小的元素应该在前,也就是说比较函数(e1,e2)=>e1-e2是正确的。

如果 compareFunction(a, b) 返回小于 0,则将 a 排序到低于 b 的索引(即 a 先出现)。 Comparision function Mozilla

const useState, useEffect, useReducer = React;

function App() 
  const [list, setList] = useState([0,1]);
  const [isAscending, toggle] = useReducer(isAscending=>!isAscending, true);
  // update the order of the list
  useEffect(()=>
    if (isAscending) 
      setList(list.sort((e1,e2)=>e1-e2));
     else 
      setList(list.sort((e1,e2)=>e2-e1));
    
  ,[list, isAscending]);
  // render
  return (
    <div>
    console.log("list[0] should be",list[0])
    <button onClick=toggle>isAscending?"Ascending":"Descending"</button>
    <h1>list[0]: list[0]</h1>
    </div>
  )


ReactDOM.render(<App/>, document.querySelector('.App'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div class='App'/>

【问题讨论】:

【参考方案1】:

我稍微简化了你的 sn-p。

你奇怪地同时使用了useStateuseReducer,而第二个是第一个的替代品。

当您有涉及多个子值的复杂状态逻辑或下一个状态取决于前一个状态时,useReducer 通常比 useState 更可取。

引自Reactjs docs

const useReducer = React;

function App() 
  const list=[0,1]
  const [isAscending, toggle] = useReducer(isAscending=>
  
    if (isAscending) 
      list.sort((e1,e2)=>e2-e1)
     else 
      list.sort((e1,e2)=>e1-e2)
    

    return !isAscending
  , list);

  // render
  return (
    <div>
    console.log("list[0] should be",list[0])
    <button onClick=toggle>isAscending?"Ascending":"Descending"</button>
    <h1>list[0]: list[0]</h1>
    </div>
  )


ReactDOM.render(<App/>, document.querySelector('.App'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div class='App'/>

所以这里的list 是文档示例的initialState。重新排序列表的逻辑现在非常简单,在 reducer 函数中。

希望对您有所帮助! ;)

【讨论】:

感谢您的回复,但这并不能解释为什么 console.log 的输出与 html 上显示的不同。 并行运行useStateuseReducer 肯定是原因... React 会批量更新状态... 解释.oO(lol) 会很危险,但请阅读that 【参考方案2】:

根据 MDN javascript 排序函数改变数组。

sort() 方法对数组的元素进行就地排序并返回 排序后的数组。

当你写作时:

setList(list.sort((e1, e2) => e1 - e2));

你在 setList 改变它之前改变列表。

setList 检查给定值是否与之前的值不同然后渲染。

但是 setList 没有什么不同,因为列表值已经通过排序方法更新了。

因此,渲染似乎落后了一步。

所以你应该写:

setList([...list].sort((e1, e2) => e1 - e2));

还有一件事:

我认为你应该改变

isAscending ? "Ascending" : "Descending"

isAscending ? "Descending" : "Ascending"

因为它是一个切换按钮

【讨论】:

"setList 检查给定值是否与之前渲染的值不同"。可以参考一下吗? 据我所知,即使你setList(previous=&gt;previous) 它仍然会触发重新渲染 setList(previous=>previous) 不会触发我们在此沙箱中看到的重新渲染:codesandbox.io/s/… 关于报价,您提到我找不到有效的参考。我通过反复试验达到了这一点。 感谢您的洞察力。我找到了参考并发布在问题中。我认为 API 文档应该更明确。【参考方案3】:

您的初始顺序是升序。当您单击按钮时,第一次切换您想要从升序到降序的顺序。

这意味着

if (isAscending) 
    // Logic for descending should come here
    setList(list.sort((e1, e2) => e2 - e1));
 else 
    // Logic for ascending should come here
    setList(list.sort((e1, e2) => e1 - e2));

检查此处的代码,让我知道这是否是您要查找的内容。 Code Sandbox: Sorted List is Not Rendering

【讨论】:

抱歉,console.log 还是和显示的不一样。【参考方案4】:

您在 useEffect 回调中关闭了 list 值,您应该将其添加到您的 dep 数组中:

function App() 
  const [list, setList] = useState([0, 1]);
  const [isAscending, toggle] = useReducer((isAscending) => !isAscending, true);

  useEffect(() => 
    setList(list.sort((e1, e2) => (!isAscending ? e1 - e2 : e2 - e1)));
  , [list, isAscending]);

  return (
    <div>
      <button onClick=toggle>
        isAscending ? "Ascending" : "Descending"
      </button>
      <pre>JSON.stringify(list)</pre>
    </div>
  );

请注意,如果您有 eslint,您应该会收到警告。

https://codesandbox.io/s/react-template-forked-08eft?file=/index.js:99-684

【讨论】:

我已更新以将列表包含为依赖项,但它似乎对原始问题中的代码 sn-p 没有任何影响。 您在排序回调中也有一个错误,请查看我的示例 对不起。我的问题中没有发现任何错误 你的答案是错误的。如果在第 14 行添加console.log(list),您会注意到记录的列表和呈现的列表是不同的。 要记录你没有放在渲染函数中的东西...登录useEffect

以上是关于React、console.log 和 render 显示不同的值的主要内容,如果未能解决你的问题,请参考以下文章

为啥 console.log() 显示 react_devtools_backend.js:4049 而不是文件和行号?

为啥 useState 不触发重新渲染?

react 组件的 render 方法中显示的 console.log 是啥?

console.log 不适用于 react-native

为啥console.log in react 运行两次

react 在creact-react-app中屏蔽console.log、解决moment.js体积过大