简体   繁体   中英

ReactJS - valid function not working when called in componentDidMount() and componentDidUpdate()

******** EDITED TO SIMPLIFY EXAMPLE AND CLARIFY REQUIREMENT AND PROBLEM **********

I'm stumped with this one, I hope someone can help.

I have a nav bar that I need to run a function on to add .active classes to li elements if they have descendants of a.active.

The menu system is a React component: -

import React, {Component} from "react";
import { Link, NavLink } from 'react-router-dom'

import {activateMenu} from './ActivateMenu'

class SidebarMenu extends React.Component {

  componentDidMount() {
    activateMenu()
  }

  componentDidUpdate() {
    activateMenu()
  }

  render() {

    const renderNavLink = (to, text, icon, renderArrow = false) => {
      return(
        <NavLink to={to}>
          <i className="bullet">{icon}</i>
          <span>{text}</span>
          {renderArrow ? <span className="pull-right-container">
                <i className="angle-left"><FaAngleLeft /></i>
              </span> : null}
        </NavLink>
      )
    }

    return (
      <ul className="sidebar-menu" data-widget="tree">

        <li className="">
          {renderNavLink('/','Home',<FaHome />)}
        </li>

        <li className="treeview">
          {renderNavLink("#",'Users',<FaGroup />, true)}
          <ul className="treeview-menu">
            <li>
              {renderNavLink(userSearchSlug,'Search',<FaSearch />)}
            </li>
          </ul>
        </li>
        <button onClick={activateMenu}>Press Me</button>
      </ul>
    )
  }
}

export default SidebarMenu

This will give me an HTML structure like this: -

<ul class="sidebar-menu tree" data-widget="tree">
  <li class="treeview">
    <a href="#">
      <i class="fa fa-dashboard"></i> <span>Links</span>
      <span class="pull-right-container">
            <i class="fa fa-angle-left pull-right"></i>
        </span>
    </a>
    <ul class="treeview-menu">
      <li>
        <a href="/link1"><i class="fa fa-circle-o"></i> Link1</a>
      </li>
      <li>
        <a href="/link2"><i class="fa fa-circle-o"></i> Link2</a>
      </li>
    </ul>
  </li>
</ul>

After React has rendered the HTML, I need to trigger a click event on the the .treeview > a node if any a.active nodes are found under .treeview-menu. So: -

<li class="treeview">
    <a href="#" *****TRIGGER CLICK EVENT*****>
        <i class="fa fa-dashboard"></i> <span>Links</span>
        <span class="pull-right-container">
            <i class="fa fa-angle-left pull-right"></i>
        </span>
    </a>
    <ul class="treeview-menu">
        <li>
            <a href="/link1"><i class="fa fa-circle-o *****.ACTIVE CLASS HERE****"></i> Link1</a>
        </li>
        <li>
            <a href="/link2"><i class="fa fa-circle-o"></i> Link2</a>
        </li>
    </ul>
</li>

activeMenu() looks like this: -

$('ul.sidebar-menu li.treeview:not(.menu-open)').has('a.active').find('a').trigger( "click" );

This function works when called from onClick() from a button on the page but it is not working in componentDidMount() and componentDidUpdate(). The function will run (tested with console.log() but not affect the HTML as it should. However, if I run it from a Button, it works perfectly. It also works perfectly when HMR runs.

I've no idea why this is happening. Does anyone have any ideas?

This is probably happening because you're selecting the element directly rather than using refs, although it's hard to say because we have no idea what $('ul.sidebar-menu .treeview a').parent().has('a.active').parent().find('.treeview a') is selecting, which is why this kind of code is an antipattern.

React may be in some state where it's not prepared to handle click events at those points. Try using something like the following:

import React, {Component} from "react";
import { Link, NavLink } from 'react-router-dom'

class SidebarMenu extends React.Component {

  constructor(props) {
    super(props);
    this.menuRefs = [];
  }

  componentDidUpdate() {
    if (this.menuRefs.length) {
      this.menuRefs[0].click();
    }
  }

  render() {

    const renderNavLink = (to, text, icon, renderArrow = false) => {
      return(
        <NavLink to={to} innerRef={ref => this.menuRefs.push(ref)}>
          <i className="bullet">{icon}</i>
          <span>{text}</span>
          {renderArrow ? <span className="pull-right-container">
                <i className="angle-left"><FaAngleLeft /></i>
              </span> : null}
        </NavLink>
      )
    }

    return (
      <ul className="sidebar-menu" data-widget="tree">

        <li className="">
          {renderNavLink('/','Home',<FaHome />)}
        </li>

        <li className="treeview">
          {renderNavLink("#",'Users',<FaGroup />, true)}
          <ul className="treeview-menu">
            <li>
              {renderNavLink(userSearchSlug,'Search',<FaSearch />)}
            </li>
          </ul>
        </li>
        <button onClick={() => this.menuRefs[0] && this.menuRefs[0].click()}>Press Me</button>
      </ul>
    )
  }
}

export default SidebarMenu

Notice

  1. Now there's an array of "menuRefs" and you just use them like normal DOM elements.
  2. We push to the menuRefs in the NavLink innerRef prop (found here)

Note however that you may want to keep a map to ensure that no duplicates get pushed into menuRefs.

To learn more about refs, visit the docs: https://reactjs.org/docs/refs-and-the-dom.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM