我正在尝试从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(quotes)返回空数组,而不是对象数组
答案 0 :(得分:2)
这是您应该如何做:
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
尚未运行。quotes
还没有length
。then
子句将运行,并且将调用setQuotes
来设置新的quotes
值。这将触发重新渲染。quotes
状态已更新为新值。useEffect
将运行,因为它正在“监听” quotes
变量中的变化。然后,您可以使用它来设置您所说的状态。答案 1 :(得分:1)
这是预期的。这是您的代码的工作方式:
quotes
和setQuotes
从useState
函数返回。useEffect
在组件安装后首次运行。 quotes
(空数组)和setQuotes
在此函数中可用。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?
});
}, []);
}
通过这种方式,您可以更紧密地控制数据,而不必将其交给生命周期方法。
答案 2 :(得分:1)
可以重构代码以使其正常工作的另一种方法:
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,您的效果不依赖于道具或状态的任何值,因此它不需要重新运行。这不是特殊情况,它直接取决于依赖项数组始终如何工作。
如果传递一个空数组([]),则效果内部的道具和状态将始终具有其初始值。通过[]作为第二个参数时,它更接近于熟悉的componentDidMount和componentWillUnmount心智模型...
我们用setState
代替setQuotes
,它用于更新引号列表,我传入了新的引号数组result.data
。
所以我传入了fetchQuote
,然后将提供给quote
组件的prop传递给了它。
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'))()