如何修复 "Warning: useLayoutEffect does nothing on the server"?
How to fix the "Warning: useLayoutEffect does nothing on the server"?
这是完整的错误:
Warning: useLayoutEffect does nothing on the server, because its
effect cannot be encoded into the server renderer's output format.
This will lead to a mismatch between the initial, non-hydrated UI and
the intended UI. To avoid this, useLayoutEffect should only be used in
components that render exclusively on the client
in ForwardRef(ButtonBase)
in WithStyles(ForwardRef(ButtonBase))
in ForwardRef(Button)
in WithStyles(ForwardRef(Button))
in form
in div
我每次 运行 我的测试都会得到它。这是我的测试
/* eslint-disable quotes */
import React from "react"
import { shallow, configure } from "enzyme"
import LoginForm from "../src/components/LoginForm"
import Button from "@material-ui/core/Button"
import Adapter from "enzyme-adapter-react-16"
import { render, fireEvent, cleanup } from "@testing-library/react"
configure({ adapter: new Adapter() })
describe("<LoginForm />", () => {
let wrapper
let usernameInput
let passwordInput
let signInButton
// Create initial props that get passed into the component
const initialProps = {
location: {
state: {
from: {
pathname: "/",
},
},
},
}
// Unit testing
describe("Unit tests", () => {
// what to do before each test
beforeEach(() => {
// Render the login form component, pass in props. (Shallow method renders the component without its children, good for unit tests.)
wrapper = shallow(<LoginForm {...initialProps} />)
usernameInput = wrapper.find("#username")
passwordInput = wrapper.find("#password")
signInButton = wrapper.find(Button)
})
// what to do after each test
afterEach(() => {
jest.clearAllMocks()
})
// UI Integrity test
it("should match the snapshot", () => {
// snapshots are text references of the html of the rendered component.
expect(wrapper.html()).toMatchSnapshot()
})
it("should have a username inputs", () => {
expect(usernameInput.length).toEqual(1)
})
it("should have the expected props on the username field", () => {
expect(usernameInput.props()).toEqual({
id: "username",
name: "username",
value: "",
type: "username",
onChange: expect.any(Function),
required: true,
})
})
it("should have a password field", () => {
expect(passwordInput.length).toEqual(1)
})
it("should have the expected props on the password field", () => {
expect(passwordInput.props()).toEqual({
id: "password",
name: "password",
value: "",
type: "password",
onChange: expect.any(Function),
required: true,
})
})
it("should have a submit button", () => {
expect(signInButton.length).toEqual(1)
})
it("should have the expected props on the button", () => {
expect(signInButton.props()).toEqual({
type: "button",
variant: "contained",
style: expect.objectContaining({
marginTop: "10px",
}),
onClick: expect.any(Function),
children: "Sign In",
})
})
})
// Integrations Testing
describe("Integrations tests", () => {
beforeEach(() => {
// Render the login form component, pass in props. (render method renders the component with its children, good for integrations tests, uses react-test-library.)
const { getByLabelText, getByText } = render(
<LoginForm {...initialProps} />
)
usernameInput = getByLabelText(/Username/i)
passwordInput = getByLabelText(/Password/i)
signInButton = getByText("Sign In")
})
afterEach(cleanup)
it("Username text change in onChange event", () => {
expect(usernameInput.value).toBe("")
fireEvent.change(usernameInput, { target: { value: "James" } })
expect(usernameInput.value).toBe("James")
})
it("Password text change in onChange event", () => {
expect(passwordInput.value).toBe("")
fireEvent.change(passwordInput, { target: { value: "mypassword" } })
expect(passwordInput.value).toBe("mypassword")
})
it("Test button submit", () => {
const mockLogin = jest.fn()
const button = shallow(<Button onClick={mockLogin} />)
button.simulate("click")
expect(mockLogin.mock.calls.length).toEqual(1)
})
})
})
我认为它与 material-ui 组件有关。我已经调查过了, 上有一个类似的问题,它说这个问题与我的项目没有的依赖关系有关。所以我认为它与使用 useEffectLayout
的 material-ui 组件有关,并且测试环境出于某种原因不喜欢那样。我 运行 正在用 yarn 和 jest yarn test
测试 运行 测试 suite.
添加
import React from "react"
React.useLayoutEffect = React.useEffect
我的 frontend/src/setupTests.js 测试文件是一种抑制玩笑警告的方法。最终,这看起来是 Material-UI 组件与 Jest 有问题的问题。
更简洁的解决方案可能是像这样使用 jest mock:
jest.mock('react', () => ({
...jest.requireActual('react'),
useLayoutEffect: jest.requireActual('react').useEffect,
}));
你应该检查 useLayoutEffect 是否可以这样使用:
import React, {useEffect, useLayoutEffect} from 'react';
const canUseDOM = typeof window !== 'undefined';
const useIsomorphicLayoutEffect = canUseDOM ? useLayoutEffect : useEffect;
然后使用 useIsomorphicLayoutEffect
而不是 useLayoutEffect
您可以通过将以下 if 语句添加到启动文件的开头来解决此问题(例如 index.js):
if (typeof document === 'undefined') {
React.useLayoutEffect = React.useEffect;
}
这将确保 SSR 使用 React.useEffect
而不是 React.useLayoutEffect
因为 document
在服务器端未定义。
这是完整的错误:
Warning: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server renderer's output format. This will lead to a mismatch between the initial, non-hydrated UI and the intended UI. To avoid this, useLayoutEffect should only be used in components that render exclusively on the client
in ForwardRef(ButtonBase) in WithStyles(ForwardRef(ButtonBase)) in ForwardRef(Button) in WithStyles(ForwardRef(Button)) in form in div
我每次 运行 我的测试都会得到它。这是我的测试
/* eslint-disable quotes */
import React from "react"
import { shallow, configure } from "enzyme"
import LoginForm from "../src/components/LoginForm"
import Button from "@material-ui/core/Button"
import Adapter from "enzyme-adapter-react-16"
import { render, fireEvent, cleanup } from "@testing-library/react"
configure({ adapter: new Adapter() })
describe("<LoginForm />", () => {
let wrapper
let usernameInput
let passwordInput
let signInButton
// Create initial props that get passed into the component
const initialProps = {
location: {
state: {
from: {
pathname: "/",
},
},
},
}
// Unit testing
describe("Unit tests", () => {
// what to do before each test
beforeEach(() => {
// Render the login form component, pass in props. (Shallow method renders the component without its children, good for unit tests.)
wrapper = shallow(<LoginForm {...initialProps} />)
usernameInput = wrapper.find("#username")
passwordInput = wrapper.find("#password")
signInButton = wrapper.find(Button)
})
// what to do after each test
afterEach(() => {
jest.clearAllMocks()
})
// UI Integrity test
it("should match the snapshot", () => {
// snapshots are text references of the html of the rendered component.
expect(wrapper.html()).toMatchSnapshot()
})
it("should have a username inputs", () => {
expect(usernameInput.length).toEqual(1)
})
it("should have the expected props on the username field", () => {
expect(usernameInput.props()).toEqual({
id: "username",
name: "username",
value: "",
type: "username",
onChange: expect.any(Function),
required: true,
})
})
it("should have a password field", () => {
expect(passwordInput.length).toEqual(1)
})
it("should have the expected props on the password field", () => {
expect(passwordInput.props()).toEqual({
id: "password",
name: "password",
value: "",
type: "password",
onChange: expect.any(Function),
required: true,
})
})
it("should have a submit button", () => {
expect(signInButton.length).toEqual(1)
})
it("should have the expected props on the button", () => {
expect(signInButton.props()).toEqual({
type: "button",
variant: "contained",
style: expect.objectContaining({
marginTop: "10px",
}),
onClick: expect.any(Function),
children: "Sign In",
})
})
})
// Integrations Testing
describe("Integrations tests", () => {
beforeEach(() => {
// Render the login form component, pass in props. (render method renders the component with its children, good for integrations tests, uses react-test-library.)
const { getByLabelText, getByText } = render(
<LoginForm {...initialProps} />
)
usernameInput = getByLabelText(/Username/i)
passwordInput = getByLabelText(/Password/i)
signInButton = getByText("Sign In")
})
afterEach(cleanup)
it("Username text change in onChange event", () => {
expect(usernameInput.value).toBe("")
fireEvent.change(usernameInput, { target: { value: "James" } })
expect(usernameInput.value).toBe("James")
})
it("Password text change in onChange event", () => {
expect(passwordInput.value).toBe("")
fireEvent.change(passwordInput, { target: { value: "mypassword" } })
expect(passwordInput.value).toBe("mypassword")
})
it("Test button submit", () => {
const mockLogin = jest.fn()
const button = shallow(<Button onClick={mockLogin} />)
button.simulate("click")
expect(mockLogin.mock.calls.length).toEqual(1)
})
})
})
我认为它与 material-ui 组件有关。我已经调查过了,useEffectLayout
的 material-ui 组件有关,并且测试环境出于某种原因不喜欢那样。我 运行 正在用 yarn 和 jest yarn test
测试 运行 测试 suite.
添加
import React from "react"
React.useLayoutEffect = React.useEffect
我的 frontend/src/setupTests.js 测试文件是一种抑制玩笑警告的方法。最终,这看起来是 Material-UI 组件与 Jest 有问题的问题。
更简洁的解决方案可能是像这样使用 jest mock:
jest.mock('react', () => ({
...jest.requireActual('react'),
useLayoutEffect: jest.requireActual('react').useEffect,
}));
你应该检查 useLayoutEffect 是否可以这样使用:
import React, {useEffect, useLayoutEffect} from 'react';
const canUseDOM = typeof window !== 'undefined';
const useIsomorphicLayoutEffect = canUseDOM ? useLayoutEffect : useEffect;
然后使用 useIsomorphicLayoutEffect
useLayoutEffect
您可以通过将以下 if 语句添加到启动文件的开头来解决此问题(例如 index.js):
if (typeof document === 'undefined') {
React.useLayoutEffect = React.useEffect;
}
这将确保 SSR 使用 React.useEffect
而不是 React.useLayoutEffect
因为 document
在服务器端未定义。