当没有任何东西可用来触发组件的重新渲染时,我很难弄清楚正在发生什么。
Events.js
当我从useState()
中删除Event.js
时,它会渲染两次,但是我需要保留它。当我在事件组件中使用useEffect()
时,将第四次渲染。
我只是保留了虚拟数据以填补空缺,并尝试删除React.memo
,什么都没有发生。我认为是Event.js
组件的问题。我也在使用Context API,但是第四次渲染时间太多了。
useEffect从localStorage中获取了一些值,我无法访问该直接值,因为默认情况下该值未定义
此处的沙盒代码:https://codesandbox.io/s/event-manager-reactjs-nbz8z?file=/src/Pages/Events/Events.js
App.js
文件位于Events.js
示例代码如下
Event.js(子组件)
/Pages/Events/Events.js
App.js(父组件)
function Events() {
// Sate Managing
const [allEvents, setAllEvents] = React.useState(null);
console.log('Rendering EventsJs', allEvents);
React.useEffect(() => {
setAllEvents(['apple', 'banana']);
}, []);
return (
<div className="events">
{ console.log('Event Rendered.js =>') }
</div>
)
}
export default React.memo(Events, (prevProps, nextProps) => {
return true;
} );
错误:
答案 0 :(得分:3)
您的应用运行正常。它正在渲染应有的状态。我们知道:
只要它的 props 或 state 发生变化,React组件就会重新渲染。
react component lifecycle order是:
在下面的示例中,它正在渲染 2次,这是正确的:
[]
['apple', 'banana']
function Events() {
const [allEvents, setAllEvents] = React.useState([]);
console.log('Event Rendered', allEvents);
useEffect(() => {
setAllEvents(['apple', 'banana']);
}, []);
return <>Events</>;
}
关于使用React.memo:
React.memo仅检查道具的更改。如果包装在React.memo中的功能组件在其实现中具有 useState 或 useContext 钩子,则当状态或上下文更改时,它仍将重新呈现。
由于状态的更改,您不能使用React.memo
跳过重新渲染。您只能进行优化,以跳过由于 props 的更改而导致的重新渲染。
但是在上面的示例中,您没有从父级组件传递的道具,传递给Events
的道具只有react-router传递的道具,即路线道具。因此,无需使用React.memo。
这里是sandbox,请检查console.logs。您只会看到3条日志:“应用程序渲染”,“具有初始状态的事件渲染”,“具有新状态的事件渲染”。
如果我们从 index.html 中删除StrictMode,然后在组件中添加以下console.logs:
App.js --> console.log('App rendered')
Evenets.js --> console.log('Event rendered', allEvents, isLoading) // (allEvents and isLoading are state variables here)
并转到http:// localhost:3000,我们看到1条日志:
App Rendered
现在单击“事件”,我们将看到3条日志:
1: Event Rendered, [], true
2: Event Rendered, [{}, ... 54 items], true
3: Event Rendered, [{}, ... 54 items], false
这是正确的行为(请参阅上面编写的生命周期顺序):
[]
,true
)渲染54 items
)和旧的isLoading(true
)渲染54 items
)和新的isLoading(false
)渲染以下是现在要问的正确问题:
为什么第二和第三渲染(日志)是分开的,难道不应该将它们分批(合并)并一起应用,因为它们是在同一函数中编写的?
fetch('url').then(() => {
// ... code here
setAllEvents([...events])
setLoading(false)
})
答案:
否,它们不会按上述代码批量处理。如Dan Abramov所述:
这是实现细节,在将来的版本中可能会更改。
在当前版本中,如果您在React事件处理程序中,它们将被一起批处理。 React批处理在React事件处理程序期间完成的所有setState,并在退出其自己的浏览器事件处理程序之前应用它们。
在当前版本中,事件处理程序外部(例如,网络响应中)的几个setState将不会被批处理。因此,在这种情况下,您将获得两次重新展示。
存在一个临时批处理API 。如果您写
ReactDOM.unstable_batchedUpdates(() => { this.fn1(); });
,则两个呼叫将被分批处理。但是我们希望将来会删除此API,而是默认情况下对所有内容进行批处理。
因此,您可以编写(在fetch的内部),如果需要,它将保存1个渲染:
ReactDOM.unstable_batchedUpdates(() => {
setAllEvents([...events])
setLoading(false)
})
以上引用中的React事件处理程序是什么?
答案: foo
在下面的示例中。这两个设置状态将被分批处理。
const foo = () => {
setAllEvents([
{ _id: '5ede5af03915bc469a9d598e', title: 'jfklsd', },
])
setLoading(false)
}
<button onClick={foo}>CLICK</button>
它会更新HTML DOM渲染次数(打印console.log)吗?
答案: 否。在更新真实DOM之前,React会比较计算出的虚拟DOM,因此只有那些更改会被应用到真实DOM,这是更新UI所必需的。
为什么在使用StrictMode
时渲染翻了一番?
答案::是的,StrictMode
将有意地双重调用{render}和其他一些生命周期方法来detect side-effects。严格模式检查仅在开发模式下运行;它们不会影响生产。
答案 1 :(得分:2)
实际上这是由您使用React.memo
引起的,它的第二个参数称为areEqual
,并且您传入了() => false
,所以您基本上是在告诉React道具总是变化。因此,每当App重新投放时,Events也会重新投放。
答案 2 :(得分:1)
您应该让React.memo
检查道具更改。通过传递() => false
,实际上是在说它的道具总是在变化(它们从不相等)。
export default React.memo(Events);
这是工作中的example。