使用挂钩+回调

时间:2019-10-27 01:53:18

标签: reactjs react-hooks

我当前正在转换此开源模板(React + Firebase + Material UI)。如果您查看代码库的许多部分,您会注意到在更改状态变量之后,将会有一个回调。这是SignUpDialog.js文件中的signUp方法的一个示例:

signUp = () => {
const {
  firstName,
  lastName,
  username,
  emailAddress,
  emailAddressConfirmation,
  password,
  passwordConfirmation
} = this.state;

const errors = validate({
  firstName: firstName,
  lastName: lastName,
  username: username,
  emailAddress: emailAddress,
  emailAddressConfirmation: emailAddressConfirmation,
  password: password,
  passwordConfirmation: passwordConfirmation
}, {
  firstName: constraints.firstName,
  lastName: constraints.lastName,
  username: constraints.username,
  emailAddress: constraints.emailAddress,
  emailAddressConfirmation: constraints.emailAddressConfirmation,
  password: constraints.password,
  passwordConfirmation: constraints.passwordConfirmation
});

if (errors) {
  this.setState({
    errors: errors
  });
} else {
  this.setState({
    performingAction: true,

    errors: null
  }, () => {        //!HERE IS WHERE I AM CONFUSED
    authentication.signUp({
      firstName: firstName,
      lastName: lastName,
      username: username,
      emailAddress: emailAddress,
      password: password
    }).then((value) => {
      this.props.dialogProps.onClose();
    }).catch((reason) => {
      const code = reason.code;
      const message = reason.message;

      switch (code) {
        case 'auth/email-already-in-use':
        case 'auth/invalid-email':
        case 'auth/operation-not-allowed':
        case 'auth/weak-password':
          this.props.openSnackbar(message);
          return;

        default:
          this.props.openSnackbar(message);
          return;
      }
    }).finally(() => {
      this.setState({
        performingAction: false
      });
    });
  });
}

};

使用钩子,我在else语句中尝试了类似的方法...

setPerformingAction(true)
setErrors(null), () => {...}

老实说,我并不是最擅长回调的人。我认为这样做是在设置状态后调用以下方法。也就是说,根据eslint的说法,这是不正确的,我希望看看是否有人可以提供帮助。谢谢,布伦南。

3 个答案:

答案 0 :(得分:1)

如果我正确理解了您的问题,那么您想知道如何实现基于类的setState回调所提供的相同行为,但是要使用功能组件。

对功能组件的思考与对基于类的组件的思考是另一回事。最简单的表达方式是,基于类的组件更为必要,而钩子/功能组件则更具声明性。

useEffect钩子需要一个依赖项数组(}, [clicks])结尾的部分是依赖项数组)-每当更改依赖项数组中包含的变量时,useEffect方法被触发。

这意味着您可以以类似于useEffect回调的方式使用setState。钩子使您可以专注于并非常精细地控制非常特定的部分状态。

This是签出的好线程-更具体地说,是对基于类(setState)和基于钩子(useState)范式之间差异的很好解释。

下面的示例演示如何使用钩子/功能组件来实现类似于“回调”行为的东西。

const { render } = ReactDOM;
const { Component, useState, useEffect } = React;

/**
 * Class based with setState
 */
class MyClass extends Component {
  state = {
    clicks: 0,
    message: ""
  }

  checkClicks = () => {
    let m = this.state.clicks >= 5  ? "Button has been clicked at least 5 times!" : "";
    this.setState({ message: m });
  }

  handleIncrease = event => {
    this.setState({
      clicks: this.state.clicks + 1
    }, () => this.checkClicks());
  }
  
  handleDecrease = event => {
    this.setState({
      clicks: this.state.clicks - 1
    }, () => this.checkClicks());
  }
  
  render() {
    const { clicks, message } = this.state;
    
    return(
      <div>
        <h3>MyClass</h3>
        <p>Click 'Increase' 5 times</p>
        <button onClick={this.handleIncrease}>Increase</button>
        <button onClick={this.handleDecrease}>Decrease</button>
        <p><b><i>MyClass clicks:</i></b> {clicks}</p>
        <p>{message}</p>
      </div>
    );
  }
}


/**
 * Function based with useState and useEffect
 */
function MyFunction() {
  const [clicks, setClicks] = useState(0);
  const [message, setMessage] = useState("");
  
  useEffect(() => {
    let m = clicks >= 5 ? "Button has been clicked at least 5 times!" : "";
    setMessage(m);
  }, [clicks]);
  
  const handleIncrease = event => setClicks(clicks + 1);
  const handleDecrease = event => setClicks(clicks - 1);
  
  return(
    <div>
      <h3>MyFunction</h3>
      <p>Click 'Increase' 5 times</p>
      <button onClick={handleIncrease}>Increase</button>
      <button onClick={handleDecrease}>Decrease</button>
      <p><b><i>MyFunction clicks:</i></b> {clicks}</p>
      <p>{message}</p>
    </div> 
  );
}


function App() {
  return(
    <div>
      <MyClass />
      <hr />
      <MyFunction />
    </div>
  );
}


render(<App />, document.body);
p {
  margin: 1px;
}

h3 {
  margin-bottom: 2px;
}

h3 {
  margin-top: 1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>

答案 1 :(得分:0)

您可以在处理程序中多次使用useState的setter,但您应该传递function to the setter而不是仅仅进行设置。

如果您使用useEffect创建处理程序,这还将解决缺少依赖项的问题。

const [state, setState] = useState({ a: 1, b: 2 });
const someHandler = useCallback(
  () => {
    //using callback method also has the linter
    //  stop complaining missing state dependency
    setState(currentState => ({ ...currentState, a: currentState.a + 1 }));
    someAsync.then(result =>
      setState(currentState => ({
        ...currentState,
        b: currentState.a * currentState.b,
      }))
    );
  },
  //if I would do setState({ ...state, a: state.a + 1 });
  //  then the next line would need [state] because the function
  //  has a dependency on the state variable. That would cause
  //  someHandler to be re created every time state changed
  //  and can make useCallback quite pointless
  [] //state dependency not needed
);

请注意,该组件实际上可以在异步工作完成之前被卸载,如果在卸载组件时调用状态设置器,则会引起警告,因此最好wrap the setter

上次我告诉您,检查挂载是没有意义的,因为您在App组件中进行了检查,并且该组件永不卸载(除非您可以在代码中向我展示可以卸载的位置),但是该组件看起来可能卸载。

答案 2 :(得分:0)

在这种情况下,如果要在执行操作后出现任何错误的情况下运行某些回调函数,则可以在这种情况下使用useEffect,因为react挂钩不接受setState那样的第二个可选回调函数。

您可以将错误添加到useEffect的依赖项数组中,并在useEffect中编写您的回调函数。如果有任何错误,这可能意味着要运行一些功能。

performAnAction = () => {

   if (actionWentWrong) {
     setError();  // maintained in state using useState  ( const [error, setError] = useState(false);
   }

}

useEffect(() => {

   // inside of this function if there are any errors do something
   if(error) {
     // do something which you were previously doing in callback function
   }
}, [error]); // this useEffect is watching if there is any change to this error state