状态返回空数组
State returning empty array
我正在尝试从 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) 返回空数组而不是对象数组
这是意料之中的。以下是您的代码的工作原理:
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() {
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
价值。这将触发 re-render.
- 第二次渲染:现在
quotes
状态已更新为新值。
- 第二次渲染后: 只有第二个
useEffect
会 运行,因为它是 "listening" quotes
的变化刚刚改变的变量。然后你可以像你说的那样用它来设置一些状态。
重构代码以使其正常工作的另一种方法:
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
作为该空数组中的第一个元素,因为它在我的示例中作为 prop 传递,但在其他示例中它不是,因此它们有一个空数组。
如果你想理解为什么我们使用空数组作为第二个参数,我认为最好的解释方式是引用 Hooks API:
如果你想运行一个效果并且只清理一次(在挂载和卸载时),你可以传递一个空数组([])作为第二个参数。这告诉 React 你的效果不依赖于 props 或 state 的任何值,所以它永远不需要 re-run。这不是作为特殊情况处理的——它直接遵循 dependencies 数组始终如何工作。
如果传递一个空数组([]),效果中的道具和状态将始终具有其初始值。虽然将 [] 作为第二个参数传递更接近熟悉的 componentDidMount 和 componentWillUnmount 心智模型...
我们调用 setQuotes
代替 setState
,这用于更新引号列表,我传入了新的引号数组 result.data
.
所以我传入了fetchQuote
,然后将提供给quote
组件的prop传给了它。
useEffect
中空数组的第二个参数很强大,不好解释and/or大家马上就明白了。例如,如果你做这样的事情 useEffect(() => {})
没有空数组作为第二个参数,那么 useEffect
函数将向 JSON 服务器端点或其他任何东西发出 non-stop 请求.
如果您将 useEffect(() => {}, [])
与空数组一起使用,它只会被调用一次,这与在 class-based 组件中使用 componentDidMount
相同。
在我上面给出的示例中,我正在检查以限制 useEffect
被调用的频率,我传入了道具的值。
我没有将异步函数放在 useEffect
中的原因是因为据我所知,如果我们传递异步函数或 returns 一个Promise,至少根据我过去看到的错误。
话虽如此,该限制有一个解决方法,如下所示:
useEffect(
() => {
(async quote => {
const result = await axios.get(quotesURL);
setQuotes(result.data);
})(quote);
},
[quote]
);
这是一个更令人困惑的语法,但它应该可以工作,因为我们正在定义一个函数并立即调用它。类似于这样的东西:
(() => console.log('howdy'))()
我正在尝试从 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) 返回空数组而不是对象数组
这是意料之中的。以下是您的代码的工作原理:
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() {
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
价值。这将触发 re-render. - 第二次渲染:现在
quotes
状态已更新为新值。 - 第二次渲染后: 只有第二个
useEffect
会 运行,因为它是 "listening"quotes
的变化刚刚改变的变量。然后你可以像你说的那样用它来设置一些状态。
重构代码以使其正常工作的另一种方法:
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
作为该空数组中的第一个元素,因为它在我的示例中作为 prop 传递,但在其他示例中它不是,因此它们有一个空数组。
如果你想理解为什么我们使用空数组作为第二个参数,我认为最好的解释方式是引用 Hooks API:
如果你想运行一个效果并且只清理一次(在挂载和卸载时),你可以传递一个空数组([])作为第二个参数。这告诉 React 你的效果不依赖于 props 或 state 的任何值,所以它永远不需要 re-run。这不是作为特殊情况处理的——它直接遵循 dependencies 数组始终如何工作。
如果传递一个空数组([]),效果中的道具和状态将始终具有其初始值。虽然将 [] 作为第二个参数传递更接近熟悉的 componentDidMount 和 componentWillUnmount 心智模型...
我们调用 setQuotes
代替 setState
,这用于更新引号列表,我传入了新的引号数组 result.data
.
所以我传入了fetchQuote
,然后将提供给quote
组件的prop传给了它。
useEffect
中空数组的第二个参数很强大,不好解释and/or大家马上就明白了。例如,如果你做这样的事情 useEffect(() => {})
没有空数组作为第二个参数,那么 useEffect
函数将向 JSON 服务器端点或其他任何东西发出 non-stop 请求.
如果您将 useEffect(() => {}, [])
与空数组一起使用,它只会被调用一次,这与在 class-based 组件中使用 componentDidMount
相同。
在我上面给出的示例中,我正在检查以限制 useEffect
被调用的频率,我传入了道具的值。
我没有将异步函数放在 useEffect
中的原因是因为据我所知,如果我们传递异步函数或 returns 一个Promise,至少根据我过去看到的错误。
话虽如此,该限制有一个解决方法,如下所示:
useEffect(
() => {
(async quote => {
const result = await axios.get(quotesURL);
setQuotes(result.data);
})(quote);
},
[quote]
);
这是一个更令人困惑的语法,但它应该可以工作,因为我们正在定义一个函数并立即调用它。类似于这样的东西:
(() => console.log('howdy'))()