React中this.state初始化遇空值,改用setState触发重复渲染的解决方法
问题解决:Next.js _app组件state初始化异常与重复渲染问题
一、constructor中state初始化无效的原因及修复
1. 修复事件绑定错误
你的componentWillUnmount方法中移除事件监听时,未通过this访问类方法,导致运行报错,可能间接引发state异常。修改如下:
componentWillUnmount(){ if (typeof window !== 'undefined') { // 避免服务端执行window相关代码报错 window.removeEventListener('resize', this.handleResize, false); window.removeEventListener('online', this.handleOnlineChange, false); window.removeEventListener('offline', this.handleOnlineChange, false); } }
2. 改用类属性初始化state
在Next.js的SSR场景下,改用类属性初始化state可避免部分初始化阶段的兼容问题:
export default class MyApp extends App { // 类属性初始化state,自动兼容服务端/客户端 state = { store: createStore(Mystore, composeWithDevTools(applyMiddleware(thunk))), onLine: typeof window !== 'undefined' ? window.navigator.onLine : null, Loginstatus: null, mobile: typeof window !== 'undefined' ? window.innerWidth < 700 : null, loading: typeof window !== 'undefined' ? window.innerHeight < 175 : true }; constructor(props){ super(props); // 此处不再赋值state } // ... 其他方法保持不变 }
二、减少不必要的重渲染
1. 合并初始化状态的setState调用
在checkLog方法中,仅更新需要异步获取的Loginstatus,其余状态已在初始化时完成设置,避免重复更新触发重渲染:
async checkLog() { try { // 无需用Promise.resolve包裹,直接await异步方法 const value = await AuthControl(); this.setState({ Loginstatus: value.data }); } catch (err) { this.setState({ Loginstatus: err }); } }
2. 给resize事件添加防抖处理
resize事件会频繁触发,通过防抖函数限制setState的调用频率:
// 封装防抖函数 debounce = (func, delay = 200) => { let timeoutId; return (...args) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => func.apply(this, args), delay); }; }; componentDidMount(){ if (typeof window !== 'undefined') { window.addEventListener('online', this.handleOnlineChange, false); window.addEventListener('offline', this.handleOnlineChange, false); // 使用防抖后的resize处理函数 const debouncedResize = this.debounce(this.handleResize); window.addEventListener('resize', debouncedResize, false); // 缓存防抖函数,用于卸载时移除监听 this.debouncedResize = debouncedResize; this.checkLog(); } } componentWillUnmount(){ if (typeof window !== 'undefined') { window.removeEventListener('resize', this.debouncedResize, false); window.removeEventListener('online', this.handleOnlineChange, false); window.removeEventListener('offline', this.handleOnlineChange, false); } }
3. 优化加载判断逻辑
原判断条件过于严格(比如离线时会一直显示加载),调整为仅在必要状态未就绪时显示加载:
render() { const { Component, pageProps } = this.props; // 仅在组件未就绪、加载中或登录状态未获取时显示加载 if (!Component || !pageProps || this.state.loading || this.state.Loginstatus === null) { return( <> <Head> <title>Loading</title> <meta name="description" content="Generated by Imtaki" /> <link rel="icon" href="/favicon.ico" /> </Head> <MyLoading/> </> ) } const {Loginstatus, mobile, store} = this.state; return( <Provider store={store}> <Head> <title>Imtaki</title> <meta name="description" content="Generated by Imtaki" /> <link rel="icon" href="/favicon.ico" /> </Head> <Header logstatus={Loginstatus} winWidth={mobile}/> <Component logstatus={Loginstatus} winWidth={mobile} {...pageProps} /> <Footer logstatus={Loginstatus} mobile={mobile} loc={"body"}/> </Provider> ) }
三、Next.js中store与状态的最佳实践
如果希望进一步优化,可以在getInitialProps中提前创建store并获取登录状态,减少客户端的异步请求和重渲染:
static async getInitialProps({ Component, ctx }) { // 创建store,服务端每个请求独立创建,避免状态污染 const allEnhancers = compose( composeWithDevTools(applyMiddleware(thunk)) ); const store = createStore(Mystore, allEnhancers); let Loginstatus = null; try { if (ctx.req) { // 服务端登录验证逻辑(比如从cookie中获取登录状态) } else { // 客户端执行登录验证 const value = await AuthControl(); Loginstatus = value.data; } } catch (err) { Loginstatus = err; } // 获取页面组件的props const pageProps = Component.getInitialProps ? await Component.getInitialProps(ctx) : {}; return { pageProps, store, Loginstatus }; } constructor(props){ super(props); const { store, Loginstatus } = props; this.state = { store, onLine: typeof window !== 'undefined' ? window.navigator.onLine : null, Loginstatus, mobile: typeof window !== 'undefined' ? window.innerWidth < 700 : null, loading: typeof window !== 'undefined' ? window.innerHeight < 175 : true }; }
内容的提问来源于stack exchange,提问作者imtaki company
相关产品推荐
相关产品推荐

