一次redux presist导致的ssr问题


问题

在使用redux presist的时候,如果这样写StoreProvider:

"use client"

import { persistor, store } from "@/store"
import { ReactNode } from "react"
import { Provider } from "react-redux"
import { PersistGate } from "redux-persist/integration/react"

export default function StoreProvider({ children }: { children: ReactNode }){
    return (
        <Provider store={store}>
            <PersistGate loading={null} persistor={persistor}>
                {children}
            </PersistGate>
        </Provider>
    )
}

会导致children无法在服务端被渲染。

解决方案

来自nuriun方法

改写成:

"use client"

import { persistor, store } from "@/store"
import { ReactNode } from "react"
import { Provider } from "react-redux"
import { PersistGate } from "redux-persist/integration/react"

export default function StoreProvider({ children }: { children: ReactNode }){
    return (
        <Provider store={store}>
            <PersistGate loading={null} persistor={persistor}>
                {() => children}
            </PersistGate>
        </Provider>
    )
}

原理

PresistGate使用了bootstrapped这个state,并且在componentDidMount中设置。

componentDidMount并不在服务端运行。于是就渲染了loading中的内容。

代码:

  componentDidMount() {
    this._unsubscribe = this.props.persistor.subscribe(
      this.handlePersistorState
    )
    this.handlePersistorState()
  }

  handlePersistorState = () => {
    const { persistor } = this.props
    let { bootstrapped } = persistor.getState()
    if (bootstrapped) {
      if (this.props.onBeforeLift) {
        Promise.resolve(this.props.onBeforeLift())
          .finally(() => this.setState({ bootstrapped: true }))
      } else {
        this.setState({ bootstrapped: true })
      }
      this._unsubscribe && this._unsubscribe()
    }
  }
  render() {
    if (process.env.NODE_ENV !== 'production') {
      if (typeof this.props.children === 'function' && this.props.loading)
        console.error(
          'redux-persist: PersistGate expects either a function child or loading prop, but not both. The loading prop will be ignored.'
        )
    }
    if (typeof this.props.children === 'function') {
      return this.props.children(this.state.bootstrapped)
    }

    return this.state.bootstrapped ? this.props.children : this.props.loading
  }cover

但是如果children是一个funcion,就会执行:

    if (typeof this.props.children === 'function') {
      return this.props.children(this.state.bootstrapped)
    }

这与this.state.bootstrapped无关。