首页 > 解决方案 > useEffect hook 在路由更改时修复依赖数组

问题描述

我正面临功能组件的问题。我正在使用一个react-router-dom包来处理路线。我所拥有的是一个包含链接和按钮的列表,当我们单击按钮时,会打开一个下拉列表。此下拉列表还包含链接。

我如何管理这些下拉菜单的状态?

我将每个下拉列表的状态视为一个单独的对象,并且onClick侦听器将key/property作为具有布尔值的参数传递。基于该值,将在下拉列表中添加一个类。

我已经在类组件的帮助下实现了这一点。

类组件:

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

class ToggleClass extends Component {
    state = {};

    toggle = dropdownName => {
        // If it has the true value then turn into false, It is a behavior of a toggle.
        if (this.state[dropdownName]) {
            this.setState({ [dropdownName]: false });
        }
        // If there is not any dropdown is opened it means state object is empty
        // Object.keys will return an empty array then please open that dropdown.
        else if (Object.keys(this.state).length === 0) {
            this.setState({ [dropdownName]: true });
        }
        // Else block will ony trigger when the user clicked on other dropdown,
        // then close the last dropdown and just open the new one
        else {
            Object.keys(this.state).forEach(i => {
                this.setState({ [i]: false });
            });
            this.setState({ [dropdownName]: true });
        }
    };

    onRouteChanged = () => {
        // Take a state object and convert all its keys into an array
        // and set it to false whenever a route changes.
        Object.keys(this.state).forEach(i => {
            // pageMenu: false
            this.setState({ [i]: false });
        });
    };

    componentDidUpdate(prevProps, prevState, snapshot) {
        if (this.props.location !== prevProps.location) {
            this.onRouteChanged();
        }
    }

    componentDidMount() {
        this.onRouteChanged();
    }

    render() {
        return (
            <ul className="list">
                <li>
                    <Link to="/link1">Link 1</Link>
                </li>

                <li>
                    <button onClick={() => this.toggle("dropdown1")}>Dropdown 1</button>

                    <ul className={this.state["dropdown1"] ? "open" : "hide"}>
                        <li>
                            <Link to="/link2">Link 2</Link>
                        </li>
                        <li>
                            <Link to="/link3">Link 3</Link>
                        </li>
                    </ul>
                </li>

                <li>
                    <button onClick={() => this.toggle("dropdown2")}>Dropdown 2</button>

                    <ul className={this.state["dropdown2"] ? "open" : "hide"}>
                        <li>
                            <Link to="/link4">Link 4</Link>
                        </li>
                        <li>
                            <Link to="/link5">Link 5</Link>
                        </li>
                    </ul>
                </li>
            </ul>
        );
    }
}

export default withRouter(ToggleClass);

但是问题出在使用钩子的功能组件上。react-router-dom有一个名为useLocation的钩子,它返回当前的pathname. 但是onRouteChanged功能正在更新状态。我不能在useEffect钩子里面使用这个函数,它变成了一个无限循环。

useEffect(() => {
  onRouteChanged();
}, [location, onRouteChanged]);

但是如果我onRouteChanged从依赖数组中删除,那么 linter 会警告这个

React Hook useEffect has a missing dependency: 'onRouteChanged'. Either include it or remove the dependency array react-hooks/exhaustive-deps

useEffect(() => {
  onRouteChanged();
}, [location]);

功能组件:

import React, { useEffect, useState } from "react";
import { Link, useLocation } from "react-router-dom";

const ToggleFunc = () => {
  const location = useLocation();
  const [state, setState] = useState({});

  const onRouteChanged = () => {
    Object.keys(state).forEach(i => {
      setState({ [i]: false });
    });
  };

  const toggle = dropdownName => {
    if (state[dropdownName]) {
      setState({ [dropdownName]: false });
    } else if (Object.keys(state).length === 0) {
      setState({ [dropdownName]: true });
    } else {
      Object.keys(state).forEach(i => {
        setState({ [i]: false });
      });
      setState({ [dropdownName]: true });
    }
  };

  useEffect(() => {
    onRouteChanged();
  }, [location]);

  return (
    <ul className="list">
      <li>
        <Link to="/link1">Link 1</Link>
      </li>

      <li>
        <button onClick={() => toggle("dropdown1")}>Box 1</button>

        <ul className={state["dropdown1"] ? "open" : "hide"}>
          <li>
            <Link to="/link2">Link 2</Link>
          </li>
          <li>
            <Link to="/link3">Link 3</Link>
          </li>
        </ul>
      </li>

      <li>
        <button onClick={() => toggle("dropdown2")}>Box 2</button>

        <ul className={state["dropdown2"] ? "open" : "hide"}>
          <li>
            <Link to="/link4">Link 4</Link>
          </li>
          <li>
            <Link to="/link5">Link 5</Link>
          </li>
        </ul>
      </li>
    </ul>
  );
};

export default ToggleFunc;

你能指导我解决这些错误的最佳方法是什么?

完整示例 Codesandbox

标签: reactjs

解决方案


我认为您必须重构状态结构和切换实现,这是固定沙箱示例(我没有更改状态结构只是解决了您的问题)。

https://codesandbox.io/s/solo98989-q11j1


推荐阅读