为什么在响应中使用 fetch 和 settingState 的组件方法不会导致 componentDidMount()?

Why is a Component method using fetch and settingState in response not causing a componentDidMount()?

我想根据用户连接状态和用户角色动态生成菜单。我有一个 json 文件,从中可以为 React 应用程序提供所有菜单选项。问题是它确实提供了 "login" 和 "contact" 选项,这些选项不需要任何特定角色或用户登录,但是当我使用应用程序的 login() 方法登录时,其中我使用 fetch API 并在响应中设置新状态,它不会刷新菜单选项(这是在 componentDidMount() 方法中完成的。它一直为我提供登录和联系选项。我想在用户连接时将登录切换为注销。

我尝试了很多东西,但是在我的代码中放置了日志和调试器,我注意到组件在 login() 获取操作中调用的 setState 之后没有重新呈现,但状态确实正在变了。我很好奇为什么 setState 没有触发 componentDidMount()?

menu.json

[
    {
        "txt": "login",
        "idRole": null
    },
    {
        "txt": "logout",
        "idRole": null
    },
    {
        "txt": "register",
        "idRole": [
            1
        ]
    },
    {
        "txt": "profile",
        "idRole": [
            1,
            2,
            3
        ]
    },
    {
        "txt": "contact",
        "idRole": null
    }
]

App.js

import React, { Component } from 'react'
import Header from 'container/Header.js'
import Footer from './container/Footer'
import Login from './container/Login'
import menu from '../json-form-file/menu.json'

export default class App extends Component {
    constructor (props) {
        super(props)
        this.state = {
            isMenuOpen: false,
            isLoggedIn: false,
            menu: null,
            page: null,
            user: null
        }
        this.toggleMenu = this.toggleMenu.bind(this)
        this.selectPage = this.selectPage.bind(this)
        this.login = this.login.bind(this)
        this.logout = this.logout.bind(this)
    }
    toggleMenu () {
        this.setState({ isMenuOpen: !this.state.isMenuOpen })
    }
    selectPage (event) {
        this.setState({ isMenuOpen: !this.state.isMenuOpen, page: event.target.textContent })
        const toggler = document.getElementsByClassName('toggler')[0]
        toggler.checked = !toggler.checked
    }
    login (event) {
        event.preventDefault()
        const requestBody = createLoginRequestBody(Array.from(event.target.parentElement.children))
        clearLoginFields(Array.from(event.target.parentElement.children))
        if (requestBody.username !== undefined && requestBody.pwd !== undefined) {
            fetch('www.someLoginUrl.login', {
                method: 'post',
                body: JSON.stringify(requestBody),
                headers: { 'Content-Type': 'application/json'
                }
            })
                .then(response => response.json())
                .then(response => this.setState({ user: response, isLoggedIn: true, page: null }))
        }
    }
    logout (event) {
        event.preventDefault()
        const toggler = document.getElementsByClassName('toggler')[0]
        toggler.checked = !toggler.checked
        this.setState({ user: null, isLoggedIn: false, page: null, isMenuOpen: !this.state.isMenuOpen })
    }
    componentDidMount () {
        console.log('im mounting')
        const newMenu = this.refreshMenuSelection(menu)
        this.setState({ menu: newMenu })
    }
    refreshMenuSelection (list) {
        const newMenu = []
        list.map((item) => {
            if (item.txt === 'login' && this.state.isLoggedIn === false) newMenu.push(item)
            if (item.txt === 'logout' && this.state.isLoggedIn === true) newMenu.push(item)
            if (item.idRole === null && item.txt !== 'login' && item.txt !== 'logout') newMenu.push(item)
            if (this.state.user !== null && item.idRole.includes(this.state.user.id_role)) newMenu.push(item)
        })
        return newMenu
    }
    render () {
        return (
            <div>
                <Header
                    menu={this.state.menu}
                    toggleMenu={this.toggleMenu}
                    selectPage={this.selectPage}
                    logout={this.logout}
                    color={this.state.isMenuOpen ? secondaryColor : primaryColor} />
                {this.state.page === 'login' ? <Login login={this.login} /> : null}
                <Footer color={this.state.isMenuOpen ? secondaryColor : primaryColor} />
            </div>
        )
    }
}

const createLoginRequestBody = (inputs) => {
    const requestObject = {}
    inputs.map((input) => {
        if (input.id === 'username') Object.assign(requestObject, { username: input.value })
        if (input.id === 'pwd') Object.assign(requestObject, { pwd: input.value })
    })
    return requestObject
}

当用户未登录时,他只能看到登录名和联系人。登录后,他可以看到注销而不是登录、联系人以及与其角色相关的所有其他选项。

没有什么会导致 componentDidMount 再次变为 运行,它是一个生命周期挂钩,运行 在组件的生命周期中只有一次。 componentDidMount 中的所有内容只会 运行 一次(在第一次渲染之后),因此如果您需要对更改做出反应以执行命令式代码,请查看 componentDidUpdate。您还应该看看 documentation

如前所述,componentDidMount 仅在首次安装组件时运行一次。如果在第一次挂载后使用setState(),函数将不会响应它。如果你想这样做,也许你应该使用 componentDidUpdate ,它会对这种类型的变化做出反应。而且,您的代码还有另一个问题。如果你使用 setState() 并使用 componentDidUpdate 那么它将再次改变状态并再次调用该函数直到程序崩溃。因此,如果您不想造成这种情况,也可以将其删除或将其移至新的 componentDidMount 函数。

感谢所有指导我使用 componentDidUpdate() 方法的人。这段修改后的代码帮助我实现了我想要的。将来我将清理代码以删除 justLoggedIn,因为这可能不是必需的,但如果没有它,它会给我的 setState 深度错误。

    login (event) {
        event.preventDefault()
        const requestBody = createLoginRequestBody(Array.from(event.target.parentElement.children))
        clearLoginFields(Array.from(event.target.parentElement.children))
        if (requestBody.username !== undefined && requestBody.pwd !== undefined) {
            fetch('http://127.0.0.1:8080/user/login', {
                method: 'post',
                body: JSON.stringify(requestBody),
                headers: { 'Content-Type': 'application/json'
                }
            })
                .then(response => response.json())
                .then(response => this.setState({ user: response, justLoggedIn: true, isLoggedIn: true }))
        }
    }
    logout (event) {
        event.preventDefault()
        const toggler = document.getElementsByClassName('toggler')[0]
        toggler.checked = !toggler.checked
        this.setState({ user: null, justLoggedOut: true, isLoggedIn: false, isMenuOpen: !this.state.isMenuOpen })
    }
    componentDidMount () {
        if (this.state.user === null) this.setState({ menu: this.refreshMenuSelection(menu) })
    }
    componentDidUpdate () {
        if (this.state.user !== null && this.state.justLoggedIn) this.setState({ menu: this.refreshMenuSelection(menu), justLoggedIn: false, page: null })
        if (this.state.user === null && this.state.justLoggedOut) this.setState({ menu: this.refreshMenuSelection(menu), justLoggedOut: false, page: null })
    }