【问题标题】:useEffect how to compare previous and updated propsuseEffect 如何比较以前和更新的道具
【发布时间】:2019-10-11 19:25:51
【问题描述】:

我正在将基于类的 Heading 组件转换为功能组件,但该组件正在使用 3 个生命周期挂钩 componentDidMountcomponentWillUnmountcomponentDidUpdate

我替换了componentDidMountcomponentWillUnmount,但是如何替换componentDidUpdate?它将更新的prop 值与旧的prop 值进行比较。

Codesandbox link.

应用组件:

import React, { Component } from "react";
import { Heading } from "./components/Heading";
import { HeadingHook } from "./components/HeadingHook";

export class App extends Component {
  state = {
    flag: false,
    mountUnmount: true
  };

  toggleChangeFlag = () => {
    if (this.state.mountUnmount) {
      this.setState({
        flag: !this.state.flag
      });
    }
  };

  toggleMountUnmount = () => {
    this.setState({
      flag: false,
      mountUnmount: !this.state.mountUnmount
    });
  };

  render() {
    return (
      <>
        <div>
          <p>Controls :</p>
          <button onClick={this.toggleChangeFlag}>Change Flag</button>
          <button onClick={this.toggleMountUnmount}>Mount & Unmount</button>
        </div>
        {this.state.mountUnmount && <Heading flag={this.state.flag} />}
        {/* Comment above line to use useEffect Heading Component */}
        {/* {this.state.mountUnmount && <HeadingHook flag={this.state.flag} />} */}
      </>
    );
  }
}

基于类的标题组件:

import React, {Component} from 'react';

export class Heading extends Component {

    handleProps() {
        if (this.props.flag) {
            alert(this.props.flag);
        } else {
             alert(this.props.flag);
        }
    };

    componentDidMount() {
        this.handleProps();
    }

    componentWillUnmount() {
        this.handleProps();
    }

    componentDidUpdate(prevProps) {
        if (this.props.flag !== prevProps.flag) {
            this.handleProps();
        }
    }

    render() {
        return <h1>Hello World!</h1>;
    }

}

useEffect 标题组件:

import React, { useEffect } from "react";

export const HeadingHook = (props) => {
  const handleProps = () => {
    if (props.flag) {
      alert(props.flag);
    } else {
      alert(props.flag);
    }
  };

  useEffect(() => {
    handleProps();
    return () => {
      handleProps();
    };
  });

  return <h1>Hello World!</h1>;
};

【问题讨论】:

    标签: reactjs


    【解决方案1】:

    您需要做的就是将一个依赖数组传递给 useEffect 以确保效果何时运行。在您当前的实现中,useEffect 不仅在初始渲染时调用,而且在每次渲染时调用。要仅在初始渲染时调用它,您需要传递一个空数组作为第二个参数。但是,如果您还希望效果在参数更改时运行,则在依赖数组中传递该参数。

    其次,Functional 组件没有this 关键字,它们接收到的props是通过函数的参数。

    const Heading = ({flag}) => {
    
    
        useEffect(() => {
            const handleProps = () => {
              if (flag) {
                alert(flag);
              } else {
                alert(flag);
              }
             };
            handleProps();
            return () => {
                handleProps();
            }
    
        }, [flag]);
        return <h1>Hello World!</h1>;
    };
    

    【讨论】:

    • 这会导致对 handleProps 依赖的 linter 警告,解决方案是 Move it inside the useEffect callback. Alternatively, wrap the 'handleProps' definition into its own useCallback() Hook.(来自 linter)
    • 由于handleprops仅在useeffect内部使用,您可以将其移至useeffect函数回调本身和。您不需要将其作为依赖项传递
    • linter 抱怨的原因是它不知道 handleProps 是否在闭包中有一些可用的值,这些值可能会在渲染之间发生变化。我同意在这种情况下不需要此警告,但由于 handleProps 仅用于效果,您也可以在那里创建它。
    • @ShubhamKhatri 为什么handleProps函数写在useEffect钩子里面?
    • @ShubhamKhatri 在代码和框链接中,我用您的代码修改了useEffect,但每当我点击Change Flag 按钮handleProps 时,函数就像基于类的标题组件那样执行2 次而不是1 次。为什么会这样?
    【解决方案2】:

    您可以通过这种方式使用useEffect 检查道具更新

    import React, {useEffect} from 'react';
    
    const Heading = (props) => {
    
      useEffect(() => {
        const handleProps = () => {
          if (props.flag) {
            alert(props.flag);
          } else {
            alert(props.flag);
          }
        };
    
        handleProps();
        return () => {
            handleProps();
        }
      }, [props.flag]);
      return <h1>Hello World!</h1>;
    };
    

    useEffect 末尾添加[props.flag] 允许它充当依赖数组,其中useEffect 挂钩仅在组件中的prop.flag 值更改时触发。因此,当您从父组件更新或更改此值时,将像componentDidUpdate 一样触发效果。

    【讨论】:

      【解决方案3】:

      当传入新的 props 时,清理会在效果应用之前运行,清理功能将不依赖于 Flag,因此我们可以简单地拥有两个 useEffect。

      import React,{useEffect} from "react";
      
      const handleProps = (props) => {
        if (props) {
          console.log(props);
        } else {
          console.log(props);
        }
      }
      
      export const Heading = ({ flag }) => {
          useEffect(() => {
            console.log("mount")
            handleProps(flag);
          }, [flag]);
      
          useEffect(() => {
            return () => {
              console.log("unmount")
              handleProps(flag)
            };
          }, []);
      
        return <h1>Hello World!</h1>;
      };
      

      【讨论】:

        【解决方案4】:
        const prevSearchText = useRef();
        
        useEffect(() => {
        
                return (function(searchText) {
        
                    return function() {
                        prevSearchText.current = searchText;
                    }
                })(props.searchText)
        
            }, [props.searchText])
        

        在 useEffect 清理中使用闭包,并为 useEffect 提供正确的依赖项,应该会有所帮助

        【讨论】:

          【解决方案5】:

          使用新的钩子代替useEffect:

          function useComponentDidMountOrUpdate(effect, deps) {
            const prev = React.useRef(deps)
          
            React.useEffect(
              () => {
                const unmountHandler = effect(prev.current)
                prev.current = deps
                return unmountHandler
              },
              deps
            )
          }
          

          使用钩子:

          useComponentDidMountOrUpdate(
            (prevDeps) => {
              const prevDep1 = prevDeps[1]
          
              if (dep1 !== prevDep1) {
                // dep1 changed
              }
          
              return () => { /* unmount handler */ }
            },
            [dep0, dep1, dep2]
          )
          

          【讨论】:

            【解决方案6】:

            您可以将第二个参数传递给 useEffect()。第二个参数将是一个带有 flag 属性的数组。每次 flag 属性发生变化都会触发 useEffect 方法。

            useEffect(() => {
              handleProps();
              
              return () => {
                handleProps();
              }
            }, [props.flag]);
            

            【讨论】:

            • 函数组件中没有this。你不能在类组件中useEffect()
            • 谢谢@Jim,我已经更正了。
            猜你喜欢
            • 2021-04-07
            • 2022-01-05
            • 2019-04-26
            • 1970-01-01
            • 2020-01-23
            • 2021-11-13
            • 2018-09-23
            • 2021-02-02
            • 2020-09-07
            相关资源
            最近更新 更多