我当前正在转换此开源模板(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的说法,这是不正确的,我希望看看是否有人可以提供帮助。谢谢,布伦南。
答案 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