我目前正在转换这个开源模板(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的说法,这是不正确的,我希望看看是否有人能帮忙。谢谢,布伦南。
如果我正确理解你的问题,你想知道如何实现与基于类的setState
回调相同的行为,但使用功能组件。。
在功能组件中思考与在基于类的组件中思考不同。。最简单的说法是,基于类的组件更具命令性,而钩子/函数组件更具声明性。
useEffect
钩子需要一个依赖数组(}, [clicks])
末尾的部分是依赖数组(——每当依赖数组中包含的变量发生变化时,就会触发useEffect
方法。
这意味着您可以以类似于setState
回调的方式使用useEffect
。。挂钩允许您专注于状态中非常特定的部分,并对其进行细粒度的控制。
这是一个很好的线索,更具体地说,它很好地解释了基于类(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>
您可以在一个处理程序中多次使用useState的setter,但您应该将函数传递给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
);
请注意,这个组件实际上可以在异步工作完成之前卸载,如果在卸载组件时调用状态setter,则会导致警告,因此最好包装setter。
上次我告诉你,检查挂载是没有意义的,因为你在应用程序组件中检查它,而该组件永远不会卸载(除非你能在代码中显示一些可以卸载它的位置(,但这个组件看起来可能会在某个时候卸载。
在这种情况下,如果您想在执行操作后出现任何错误时运行某个回调函数,您可以在这种情况中使用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