【问题标题】:React Context and Server Side Rendering: Right way to use React Context for authenticationReact Context 和服务器端渲染:使用 React Context 进行身份验证的正确方法
【发布时间】:2022-07-11 15:42:27
【问题描述】:

我有一个需求,在多个路由中,我需要(重要的是)在服务器上渲染其中一个静态路由以提高加载速度,该路由的内容独立于其余路由,现在其余路由是相互连接的,我使用 React 上下文进行身份验证,我不想在服务器端进行身份验证检查,我只想:

If-> required route -> 显示该页面的服务器渲染内容

Else-> 像以前一样继续(我不介意这些页面的 UI 是否也被服务器渲染)

现在我的问题是,在我的情况下使用上下文的正确方法是什么。这是一些代码:

./src/index.js(客户端代码)

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import AuthProvider from "./Context/AuthContext";
import ErrorBoundary from "./ErrorBoundary/ErrorBoundary";
import { BrowserRouter } from "react-router-dom";

ReactDOM.hydrate(
  <AuthProvider>
    <ErrorBoundary>
      <BrowserRouter>
      <App />
      </BrowserRouter>
    </ErrorBoundary>
  </AuthProvider>,
  document.getElementById("root")
);

./src/server/server.js

import path from "path";
import fs from "fs";

import React from "react";
import ReactDOMServer from "react-dom/server";
import express from "express";

import App from "../src/App";
import AuthProvider from "../src/Context/AuthContext";
import ErrorBoundary from "../src/ErrorBoundary/ErrorBoundary";
import { StaticRouter } from "react-router";

const PORT = process.env.PORT || 3006;
const app = express();

app.get("*", (req, res) => {
  const markup = ReactDOMServer.renderToString(
    <AuthProvider>
      <ErrorBoundary>
        <StaticRouter location={req.url} context={{}}>
          <App />
        </StaticRouter>
      </ErrorBoundary>
    </AuthProvider>
  );
  const indexFile = path.resolve("./build/index.html");

  fs.readFile(indexFile, "utf-8", (err, data) => {
    if (err) {
      console.error("Something went wrong:", err);
      return res.status(500).send("Something went wrong");
    }

    console.log(req.url)

    return res.send(
      data.replace('<div id="root"></div>', `<div id="root">${markup}</div>`)
    );
  });
});

app.use(express.static(path.resolve(__dirname, "..", "build")));

app.listen(PORT, () => {
  console.log(`Server is listening on port ${PORT}`);
});

AuthContext.js

import React, { createContext, useState, useEffect } from "react";
import * as AuthService from "../Services/AuthService";
import loadingSvg from "../Assets/loading.gif";

export const AuthContext = createContext();

const Auth = ({ children }) => {
  const [user, setUser] = useState(null);
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [isLoaded, setIsLoaded] = useState(false);

  useEffect(() => {
    // Even if refreshed, this is the first api call that is going to happen.

    if (localStorage.getItem("user")) {
      setUser(JSON.parse(localStorage.getItem("user")));
      setIsAuthenticated(true);
      setIsLoaded(true);
    }
    AuthService.isAuthenticated()
      .then((data) => {
        console.log("isAuth :", data);
        if (data.isAuthenticated) {
          console.log("Updated Auth Check - ", data);
          setUser(data.user);
          setIsAuthenticated(data.isAuthenticated);
          localStorage.setItem("user", JSON.stringify(data.user));
          setIsLoaded(true);
        } else {
          console.log("There is some error, please login again. ");
          setIsLoaded(true);
          setIsAuthenticated(false);
          localStorage.removeItem("user");
          setUser(null);
        }
      })
      .catch((err) => {
        console.dir("console err:", err);
        console.log("There is some error, please login again. ");
        setIsLoaded(true);
        setIsAuthenticated(false);
        localStorage.removeItem("user");
        setUser(null);
      });
  }, []);

  return (
    <div>
      {!isLoaded ? (
        <div
          style={{
            display: "flex",
            justifyContent: "center",
            alignItems: "center",
            height: "100vh",
            width: "100vw",
            background: "white",
          }}
        >
        </div>
      ) : (
        <AuthContext.Provider
          value={{ user, setUser, isAuthenticated, setIsAuthenticated }}
        >
          {children}
        </AuthContext.Provider>
      )}
    </div>
  );
};

export default Auth;

另外,我正在使用 cookie 进行授权和 react-router v5。

还有一点, 当我检查页面源时,我得到以下“根”div

<div id="root"><div data-reactroot=""><div style="display:flex;justify-content:center;align-items:center;height:100vh;width:100vw;background:white"></div></div></div>

所以服务器端渲染失败(?)

【问题讨论】:

    标签: javascript reactjs react-router server-side-rendering react-context


    【解决方案1】:

    我认为只要验证码在useEffect 中,它只会在客户端运行。因此,您不必更改任何内容,您的代码应该按原样运行。

    服务器渲染将使用您传递给提供的上下文的初始值完成。然后当组件在客户端被水合时,你的 useEffect 回调将运行,并且值将被更新。

    useEffect、componentDidMount 和其他生命周期方法不在服务器端运行。所以这是延迟服务器执行某些逻辑的好方法。

    【讨论】:

      猜你喜欢
      • 2019-08-05
      • 1970-01-01
      • 2016-09-06
      • 2015-08-18
      • 2020-02-29
      • 2016-04-11
      • 2016-08-06
      • 2019-10-02
      • 2018-07-23
      相关资源
      最近更新 更多