//UPDATE:问题是在useEffect()
中设置状态后立即使用状态。
我试图升级我的一个React应用程序页面从类组件到功能组件与Hooks。然而,由于一些异步函数,我有一些问题。
旧页面的行为方式是,在componentDidMount()中,一些数据是从数据库异步获取并显示。工作正常,myName
和myValue
显示正常
// OLD APPROACH - CLASS COMPONENT
class MyPage extends Component {
constructor(props) {
super(props);
this.state = {
myName: null,
myValue: undefined,
}
}
componentDidMount = async () => {
try {
const myName = await getNameFromDatabase();
const myValue = await getValueFromDatabase();
this.setState({ myName, myValue });
} catch (error) {
alert(
"Some errors occured when fetching from DB"
);
console.error(error);
}
}
render() {
return (
<div>
<h1>{this.state.myName}</h1>
<h1>{this.state.myValue}</h1>
</div>
)
}
export default MyPage
我试着按照这个回复来更新这个页面。
// NEW APPROACH - FUNCTIONAL COMPONENT WITH HOOKS
function MyPage() {
const [myName, setMyName] = useState(null);
const [myValue, setMyValue] = useState(undefined);
useEffect(() => {
async function fetchFromDatabase() {
const myName = await getNameFromDatabase();
const myValue = await getValueFromDatabase();
setMyName(myName);
setMyValue(myValue);
}
fetchFromDatabase();
}, [])
return (
<div>
<h1>{myName}</h1>
<h1>{myValue}</h1>
</div>
)
}
但是,当我这样做时,它们不再显示。我想它们仍然是"零"。和";undefined"。显然,如果我做一个console.log()
,它们最终会被获取,但只有在没有它们的页面被渲染之后,这不是在第一种情况下发生的事情。
为什么会发生这种情况?为什么它在第一种情况下正确显示,但在第二种情况下却没有?据我所知,useEffect()
和componentDidMount()
做同样的事情。如果我想在useEffect()
内部调用异步函数,我应该继续另一种方式吗?
useEffect
钩子和状态更新没有问题。函数组件是无实例的,所以this
是未定义的。修复渲染,直接引用状态值。
处理异步代码时处理错误也是一个很好的做法。
function MyPage() {
const [myName, setMyName] = useState(null);
const [myValue, setMyValue] = useState(undefined);
useEffect(() => {
async function fetchFromDatabase() {
try {
const myName = await getNameFromDatabase();
const myValue = await getValueFromDatabase();
setMyName(myName);
setMyValue(myValue);
} catch(error) {
// handle any rejected Promises and thrown errors
}
}
fetchFromDatabase();
}, []);
return (
<div>
<h1>{myName}</h1>
<h1>{myValue}</h1>
</div>
);
}
首先,为响应指定与useState()相同的名称。试着用不同的名字。然后,将空字符串放入您的useState()默认值中,而不是null或undefined。最后,您不再需要使用this而是直接访问值。应该是这样的:
function MyPage() {
const [myName, setMyName] = useState('');
const [myValue, setMyValue] = useState('');
useEffect(() => {
async function fetchFromDatabase() {
const name = await getNameFromDatabase();
const value = await getValueFromDatabase();
setMyName(name);
setMyValue(value);
}
fetchFromDatabase();
}, [])
return (
<div>
<h1>{myName}</h1>
<h1>{myValue}</h1>
</div>
)
}
function MyPage() {
const [myName, setMyName] = useState(null);
const [myValue, setMyValue] = useState(undefined);
useEffect(() => {
(async () => {
const myName = await getNameFromDatabase();
const myValue = await getValueFromDatabase();
setMyName(myName);
setMyValue(myValue);
})();
}, []);
return (
<div>
<h1>{myName}</h1>
<h1>{myValue}</h1>
</div>
);
}
好的,所以原文中的代码是正确的正如其他人所说的那样。然而,它是我正在处理的实际代码的一个非常简化/抽象的版本。
我做错的是,我在设置后立即使用useEffect()
的状态。
像这样:
// WRONG
let fetchedName= getNameFromDatabase();
setMyName(fetchedName);
if(myName==="something") {
setMyValue(1000);
}
结论是:永远不要在useEffect()
或componentWillMount()
中设置后立即使用状态,使用中间变量
而不是做的事:
// CORRECT
let fetchedName= getNameFromDatabase();
setMyName(fetchedName);
if(fetchedName==="something") {
setMyValue(1000);
}