我正在尝试从useState钩子访问状态,但即使我修改了它,它也给了我初始状态。
const quotesURL = "https://gist.githubusercontent.com/camperbot/5a022b72e96c4c9585c32bf6a75f62d9/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json";
function QuoteGenerator() {
const [quotes, setQuotes] = useState([]);
const [currentQuote, setCurrentQuote] = useState({ quote: "", author: "" });
useEffect(() => {
axios(quotesURL)
.then(result => {
console.log(result);
setQuotes(result.data);
})
.then(() => {
console.log(quotes);
});
}, []);
console.log(引号)返回空数组而不是对象数组
以下是您应该如何操作:
const quotesURL = "https://gist.githubusercontent.com/camperbot/5a022b72e96c4c9585c32bf6a75f62d9/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json";
function QuoteGenerator() {
const [quotes, setQuotes] = useState([]);
const [currentQuote, setCurrentQuote] = useState({ quote: "", author: "" });
useEffect(() => { // THIS WILL RUN ONLY AFTER YOUR 1ST RENDER
axios(quotesURL)
.then(result => {
console.log(result);
setQuotes(result.data); // HERE YOU SET quotes AND IT WILL TRIGGER A NEW RENDER
})
}, []); // BECAUSE YOU'VE SET IT WITH '[]'
useEffect(() => { // THIS WILL RUN WHEN THERE'S A CHANGE IN 'quotes'
if (quotes.length) {
setSomeOtherState(); // YOU CAN USE IT TO SET SOME OTHER STATE
}
},[quotes]);
}
此代码的工作原理:
- 第一次渲染:你只是初始状态。
useEffects
尚未运行。 - 第一次渲染后:两种效果都将运行(按该顺序)。第一个将触发 axios 请求。第二个什么都不做,因为
quotes
还没有length
。 - Axios 请求完成:
then
子句将运行,并将调用setQuotes
来设置新的quotes
值。这将触发重新渲染。 - 第二次渲染:现在,
quotes
状态已使用新值更新
。 - 第二次渲染后:只有第二个
useEffect
会运行,因为它正在"侦听"刚刚更改的quotes
变量中的更改。然后你可以使用它来设置一些状态,就像你说的那样。
这是意料之中的。以下是代码的工作原理:
quotes
和setQuotes
从useState
函数返回。useEffect
挂载组件后首次运行。quotes
(空数组)和setQuotes
在此函数中可用。- 当您的 axios 请求完成时,您
setQuotes
.但是,有两件事:1 - 这不会立即更新状态的值。2 - 在useEffect
的上下文中,quotes
仍然是一个空数组 - 当您执行setQuotes(result.data)
时,您将创建一个新数组,并且在此上下文中无法直接访问该数组。 - 因此,
console.log(quotes);
将给出一个空数组。
取决于您尝试使用quotes
的目的。为什么不直接与result.data
合作?
更新:我正在考虑可能这样的事情:
function QuoteGenerator() {
const [quotes, setQuotes] = useState([]);
const [currentQuote, setCurrentQuote] = useState({ quote: "", author: "" });
useEffect(() => {
axios(quotesURL).then(result => {
console.log(result);
setQuotes(result.data);
setSomeOtherState(); // why not do it here?
});
}, []);
}
通过这种方式,您可以保持对数据的更严格控制,而无需将其交给生命周期方法。
重构代码以使其工作的另一种方法:
const quotesURL = "https://gist.githubusercontent.com/camperbot/5a022b72e96c4c9585c32bf6a75f62d9/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json";
function QuoteGenerator = ({ quote }) => {
const [quotes, setQuotes] = useState([]);
const [currentQuote, setCurrentQuote] = useState({ quote: "", author: "" });
const fetchQuote = async quote => {
const result = await axios.get(quotesURL);
setQuotes(result.data);
};
useEffect(() => {
fetchQuote(quote);
}, [quote]);
};
所以现在你的QuoteGenerator
功能组件内部有一个名为fetchQuote
的函数。useEffect
钩子允许我们使用类似生命周期方法的东西,有点像将componentDidMount
和componentDidUpdate
生命周期方法结合起来。在这种情况下,我调用了useEffect
,其中包含一个函数,该函数将在每次此组件最初呈现到屏幕上以及每次组件更新时运行。
您可以在其他答案中看到,第二个参数作为空数组传递。我把quote
作为该空数组中的第一个元素,因为它在我的示例中作为道具传递,但在其他人的示例中并非如此,因此它们有一个空数组。
如果你想理解为什么我们使用空数组作为第二个参数,我认为最好的解释方法是引用 Hooks API:
如果要运行效果并仅清理一次(在装载和卸载时),则可以传递空数组 ([]) 作为第二个参数。这告诉 React 你的效果不依赖于来自 props 或状态的任何值,所以它永远不需要重新运行。这不是作为特殊情况处理的 - 它直接遵循依赖关系数组始终的工作方式。
如果传递一个空数组 ([]),效果内部的 props 和状态将始终具有其初始值。虽然传递 [] 作为第二个参数更接近熟悉的组件 DidMount 和组件将卸载心智模型......
代替setState
我们调用setQuotes
,这用于更新报价列表,我传入了新的报价数组,该数组result.data
。
所以我传入了fetchQuote
,然后传递了提供给quote
组件的道具。
useEffect
中空数组的第二个参数非常强大,并且不容易立即解释和/或理解每个人。例如,如果您执行类似useEffect(() => {})
的操作,没有空数组作为第二个参数,则该函数useEffect
将向 JSON 服务器终端节点或其他任何内容发出不间断的请求。
如果将useEffect(() => {}, [])
与空数组一起使用,则只会调用一次,这与在基于类的组件中使用componentDidMount
相同。
在我上面给出的示例中,我正在建立一个检查来限制调用useEffect
的频率,我传入了道具的值。
我没有将异步函数放在useEffect
中的原因是,我的理解是,如果我们传递异步函数或返回 Promise 的函数,至少根据我过去看到的错误,我们不能使用useEffect
。
话虽如此,该限制有一个解决方法,如下所示:
useEffect(
() => {
(async quote => {
const result = await axios.get(quotesURL);
setQuotes(result.data);
})(quote);
},
[quote]
);
这是一个更令人困惑的语法,但它应该有效,因为我们正在定义一个函数并立即调用它。类似于这样的东西:
(() => console.log('howdy'))()