【问题标题】:How can I update the state of my menu only on the first time my component renders?如何仅在组件第一次呈现时更新菜单的状态?
【发布时间】:2018-03-25 21:06:40
【问题描述】:

我有一个使用参数调用的应用程序。现在,我的菜单总是加载到默认的 MainMenu。我希望能够将状态传递给 Menu.js 并从打开的不同菜单开始,而不是默认的 MainMenu。

我认为 componentWillMount() 可以让我将状态设置为我选择的菜单状态,但无论出于何种原因,它都不会更新呈现的内容。当我调用它时,this.state.Selected 成为我想要呈现的正确菜单名称......但它实际上并没有呈现 - 它最终呈现默认菜单。

所以我必须在 componentWillMount() 之后调用其他东西才能让它真正呈现?

Landing.js - 调用 Menu.js。我想先渲染菜单“FileOptions”。但是,它当前正在渲染 MainMenu。

import React, { Component } from 'react'
import sass from '../scss/application.scss'
import PropTypes from 'prop-types'
import Header from './Header'
import Menu from './Menu'
import HelpFile from './HelpFile'



class Landing extends Component {
    constructor(props) {
         super(props);
         this.state = { 
             helpFileName: 'Mainmenu',
             menuName: 'FileOptions',
         }
       }

    handleHelpChange(helpFileName) {
        this.setState( {helpFileName} );
    }

    handleMenuClick(menuName) {
        this.setState( {menuName} );
    }

    componentWillMount() {
        let hlpString = require('electron').remote.getGlobal('sharedObject').hlpOne;
        if (hlpString != null && hlpString != '.')
        {
            this.setState({
                helpFileName: hlpString
            });ss
        }
    }

    render() {  

        return (
            <div>
                <div>
                    <Header />
                </div>
                <br /><br />
                <div className="mainMenuDiv">
                    <Menu handleHelpChange={this.handleHelpChange.bind(this)} menuName={this.state.menuName}/>
                </div>
                <div className="mainContainerDiv">
                    <HelpFile name={this.state.helpFileName}/>
                </div>
            </div>

        )
    }
}


export default Landing;

菜单.js

import React, { Component } from 'react'
import sass from '../scss/application.scss'
import PropTypes from 'prop-types'



class Menu extends Component {
    constructor(props) {
         super(props);
         this.state = { 
             Selected: props.menuName,      // reads FileOptions, but still renders MainMenu
             name: ''
         }
       }

    handleChange(name) {
        this.setState({
            name: name
        });
    }

    handleClick(e, num) {
        this.setState({
            name: num
          }, () => {
            let helpFileName = num;
            helpFileName = helpFileName.toLowerCase().trim();
            //Cap the first letter in the name and add the rest of the name 
            helpFileName = helpFileName.charAt(0).toUpperCase() + helpFileName.substr(1);
            this.props.handleHelpChange(helpFileName);
          });
      }

     handleMenuClick(e, num, opt) {
        this.setState({
            name: num,
            Selected: opt
          }, () => {
            let helpFileName = num;
            helpFileName = helpFileName.toLowerCase().trim();
            //Cap the first letter in the name and add the rest of the name 
            helpFileName = helpFileName.charAt(0).toUpperCase() + helpFileName.substr(1);
            this.props.handleHelpChange(helpFileName);
          });
      }

    render() {
        const MainMenu = () => (
            <div>
                <button 
                    label="File Options"
                    //onClick={() => this.setState({ Selected: FileOptions })} 
                    onClick={(e) => this.handleMenuClick(e, 'Fileopt', FileOptions)}
                    className="aMenuButton"
                >FILE OPTIONS</button>
                <button 
                    label="Setup Options"
                    onClick={(e) => this.handleMenuClick(e, 'Setupopt', SetUpOptions)}
                    className="aMenuButton"
                >SETUP OPTIONS</button>
                <button 
                    label="Lumber Options"
                    onClick={(e) => this.handleMenuClick(e, 'Lumberopt', MoreOptions)}
                    className="aMenuButton"
                >MORE OPTIONS</button>
                <button 
                    label="Main Menu"
                    onClick={(e) => this.handleClick(e, 'Mainmenu')} 
                    className="aPrevButton"
                >MAIN MENU</button>
            </div>
        );

        const FileOptions = () => (
            <div>
                <button 
                    label="Option One"
                    onClick={(e) => this.handleClick(e, 'Option One')}
                    className="aMenuButton"
                >Option One</button>
               <button 
                    label="Option Two"
                    onClick={(e) => this.handleClick(e, 'Option Two')}
                    className="aMenuButton"
                >Option Two</button>
                <button 
                    label="Option Three"
                    onClick={(e) => this.handleClick(e, 'Option Three')}
                    className="aMenuButton"
                >Option Three</button>
                <button 
                    label="Option Four"
                    onClick={(e) => this.handleClick(e, 'Option Four')}
                    className="aMenuButton"
                >Option Four</button>
                <button 
                    label="Previous Menu"
                    onClick={() => this.setState({ Selected: MainMenu })} 
                    className="aPrevButton"
                >PREVIOUS MENU</button>
            </div>
        );

        const SetUpOptions = () => (
            <div>
                <button 
                    label="Option One"
                    onClick={(e) => this.handleClick(e, 'Option One')}
                    className="aMenuButton"
                >Option One</button>
               <button 
                    label="Option Two"
                    onClick={(e) => this.handleClick(e, 'Option Two')}
                    className="aMenuButton"
                >Option Two</button>
                <button 
                    label="Option Three"
                    onClick={(e) => this.handleClick(e, 'Option Three')}
                    className="aMenuButton"
                >Option Three</button>
                <button 
                    label="Previous Menu"
                    onClick={() => this.setState({ Selected: MainMenu })} 
                    className="aPrevButton"
                >PREVIOUS MENU</button>
            </div>
        );

        const MoreOptions = () => (
            <div>
                <button 
                    label="Option One"
                    onClick={(e) => this.handleClick(e, 'Option One')}
                    className="aMenuButton"
                >Option One</button>
               <button 
                    label="Option Two"
                    onClick={(e) => this.handleClick(e, 'Option Two')}
                    className="aMenuButton"
                >Option Two</button>
                <button 
                    label="Option Three"
                    onClick={(e) => this.handleClick(e, 'Option Three')}
                    className="aMenuButton"
                >Option Three</button>
                <button 
                    label="Option Four"
                    onClick={(e) => this.handleClick(e, 'Option Four')}
                    className="aMenuButton"
                >Option Four</button>
                <button 
                    label="Previous Menu"
                    onClick={() => this.setState({ Selected: MainMenu })} 
                    className="aPrevButton"
                >PREVIOUS MENU</button>
            </div>
        );

        const { Selected } = this.state;

        return (
            <div>   
                <div className="menuButtons">
                    {Selected === 'MainMenu' ? <MainMenu /> : <Selected /> }
                </div>
            </div>
        )
    }
}

Menu.propTypes = {  
    handleHelpChange: PropTypes.func,
    name: PropTypes.string,
    menuName: PropTypes.string
}


export default Menu;

【问题讨论】:

  • 我认为菜单中的渲染功能存在错误。您不能只将字符串更改为 React 组件。如果 Selected = "FileOptions",则不能执行 并期望它是 。我不确定它为什么会呈现 MainMenu,您可能想检查从require('electron').remote.getGlobal('sharedObject').hlpOne返回的内容@
  • sharedObject 呈现页面内容并且不会弄乱菜单。现在 渲染 MainMenu 或 FileOptions 因为 const { Selected } = this.state。当一个菜单项被点击时,状态被更新并且一个函数被调用导致组件重新渲染。整个菜单系统运行良好,它总是必须从 MainMenu 开始。希望找到一种方法从不同的菜单点开始,例如 FileOptions

标签: reactjs components render lifecycle


【解决方案1】:

来自react docs for componentWillMount

componentWillMount() 在安装发生之前立即调用。 在render()之前调用,因此同步设置状态 在此方法中不会触发重新渲染。避免引入任何 此方法中的副作用或订阅。这是唯一的 在服务器渲染上调用的生命周期钩子。一般来说,我们建议 改用constructor()

更新 1

在构造函数中设置您选择的菜单名称并从 componentWillMount 中删除 setState。

更新 2

@Yiou 注意到&lt;Selected /&gt; 不是一个组件。

class Menu extends Component {
    constructor(props) {
      super(props);
      this.state = { 
        Selected: props.menuName || '',
        name: ''
      }
    }

  // ....

  render() {

    // after you created all your menu constant implement a switch to select
    // which menu is going to render
    const { Selected } = this.state;
    let SelectedMenu;
    switch(Selected) {
      case 'MainMenu':
        SelectedMenu = MainMenu;
        break;
      case 'FileOptions':
        SelectedMenu = FileOptions;
        break;
      default:
        SelectedMenu = MainMenu;
        break;
    }

    return (
        <div>   
            <div className="menuButtons">
                {SelectedMenu()}
            </div>
        </div>
    )
  }    
}

【讨论】:

  • 我不明白,我设置了构造函数。如果我在构造函数中设置 Selected: this.props.menuName - 我会得到相同的结果。
  • @user3622460 用一段可能对你有帮助的代码更新了答案
  • 啊,这就是我在介绍componentWillMount之前的情况。不工作。如果我将 menuName 设置为“FileOptions”,菜单将无法加载。无论出于何种原因,如果我不传入 MainMenu,它将根本无法加载任何菜单。
  • 我很抱歉,但这没有意义。即使您没有向 Menu 组件发送任何道具,它也应该呈现 &lt;Selected /&gt;。你的代码中有一堆额外的s 字母。我认为它们是错字,但现在我认为它们可能是问题的原因。请检查您的代码并根据您对代码所做的任何更改更新您的问题。
  • 那些是错字 - 现在修复它们
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-21
  • 2021-09-09
  • 1970-01-01
  • 1970-01-01
  • 2021-12-22
  • 2020-05-12
  • 2021-07-10
相关资源
最近更新 更多