我正在使用 useEffect 钩子从天气 api 获取数据并正确声明依赖项。我的状态仍未更新,因此我的渲染函数出现错误。我几乎尝试了从摆脱依赖数组到在依赖数组中声明倍数的所有方法。我不知道我的功能有什么问题。API 的 JSON 响应格式如下:
{
location: {
name: "Paris",
region: "Ile-de-France",
},
current: {
last_updated_epoch: 1564279222,
last_updated: "2019-07-28 04:00",
temp_c: 16,
temp_f: 60.8,
is_day: 0,
condition: {
text: "Clear",
icon: "//cdn.apixu.com/weather/64x64/night/113.png",
code: 1000
},
wind_mph: 6.9,
wind_kph: 11.2
}
}
Run Code Online (Sandbox Code Playgroud)
这就是我的代码的样子:
const Weather = ({ capital }) => {
const [weather, setWeather] = useState(null);
useEffect(() => {
console.log("useEffect called");
const getWeather = async () => {
try {
const res = await axios.get(
`http://api.apixu.com/v1/current.json?key=53d601eb03d1412c9c004840192807&q=${capital}`
);
setWeather(res.data); …Run Code Online (Sandbox Code Playgroud) TL'DR:有没有办法在对 API 调用使用 useEffect 时悲观地操作状态更改?
假设您编写了一个显示分页/排序数据网格的组件,并且您为该组件编写了一个 useEffect 类似于这个假示例:
useEffect(() => {
fetch(`https://some-random-api.com/data/page/{paging}/sort/{sorting}`)
.then(response => response.json())
.then(data => setState({
data
}));
}, [paging, sorting]);Run Code Online (Sandbox Code Playgroud)
所以,如果我没记错的话,这将在分页或排序状态/道具(whatev)更新时获取。这意味着它在加载数据之前乐观地呈现新的排序和分页状态。基本上,该应用程序会告诉用户“我在请求的页面上,但我仍在加载它”。换句话说,应用程序状态的一部分(分页和排序)表示“我完成了”,状态的另一部分(加载)表示“我正在处理它”。
相比之下,如果我悲观地更新状态,我只会在获取之前将加载状态设置为 true,然后在收到响应时(并且仅在那时)设置分页、排序(& 数据和加载)。因此,该应用程序会告诉用户“我听到了您的意见,我正在处理它”,然后当收到成功响应时,该应用程序会说“这是请求的数据和更新的上下文(更新的页面/排序状态)”。
另一个问题是请求是否失败。在 optimisitic/useEffect 方法中,我现在需要恢复在尝试加载这些状态更改的数据之前更新的分页和排序状态。这种复杂性随着依赖项的数量而增加。另外,这里是关键(再次,如果我没记错的话)恢复这些状态将导致另一个获取(这可能会再次失败)。在悲观方法中,您只需在发生错误时设置 loading = false 并且不会进行其他状态更改,因为分页和排序仅在成功接收数据时更新。数据以悲观的方式(不乐观)保持同步,这就是为什么我认为当博客和视频在使用 useEffect 时说“从同步的角度思考”是具有讽刺意味的。
还有一件事,比如说从 useEffect 内部调用的 API(在其他一些例子中)实际上正在改变服务器端的某些东西。在这种情况下,当使用 useEffect 并应用它的乐观方法时,只会对用户撒谎。该应用程序说“好的,我更新了”。也许不吧。如果没有(如果有错误),希望有状态更新来恢复对用户撒谎的状态更改,并在恢复该状态时以某种方式避免额外的 API 调用。希望用户看到状态变回并希望有一条消息......即便如此,这是一个好的用户体验吗?
我并不是说乐观渲染总是不好的。我的意思是“我大部分时间都喜欢悲观。我如何在使用 useEffect 时做到这一点?” :)
似乎 useEffect 正在被用于和推广应用程序的大多数副作用(很多)。我并不经常发现它很有用。我是一个悲观主义者,希望获得替代方案。
更新:对于用例 1,是的,如果我在 useEffect 之外提取 search.value 并将其用作依赖项,则它可以工作。
但我在下面有一个更新的用例
用例 2:我想将 searchHits 对象传递给服务器。服务器反过来将其返回给我,并带有更新的值作为响应。如果我尝试使用 searchHits 对象,我仍然会得到无限循环
state: {
visible: true,
loading: false,
search: {
value: “”,
searchHits: {....},
highlight: false,
}
}
let val = search.value
let hits = search.searchHits
useEffect( () => {
axios.post(`/search=${state.search.value}`, {hits: hits}).then( resp => {
…do something or ..do nothing
state.setState( prevState => {
return {
…prevState,
search: {... prevState.search, hits: resp.hit}
}
})
})
}, [val, hits])
Run Code Online (Sandbox Code Playgroud)
用例 1:我想搜索一个字符串,然后在得到结果时突出显示
例如
state: {
visible: true,
loading: …Run Code Online (Sandbox Code Playgroud) 我正在开发一个从 firebase 数据库中提取数据的 react chap 应用程序。在我的“仪表板”组件中,我有一个 useEffect 钩子检查经过身份验证的用户,如果是,则从 firebase 中提取数据并设置电子邮件变量和聊天变量的状态。我使用 abortController 进行 useEffect 清理,但是每当我第一次注销并重新登录时,我都会收到内存泄漏警告。
index.js:1375 警告:无法对卸载的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要修复,请取消 useEffect 清理函数中的所有订阅和异步任务。
在仪表板中(由 Context.Consumer 创建)
最初我没有 abortController,我只是在清理时返回了一个控制台日志。做了更多的研究,发现 abortController 但是这些例子使用了 fetch 和信号,我找不到任何与 async/await 一起使用的资源。我愿意更改数据的检索方式,(无论是使用 fetch、async/await 还是任何其他解决方案)我只是无法使用其他方法使其正常工作。
const [email, setEmail] = useState(null);
const [chats, setChats] = useState([]);
const signOut = () => {
firebase.auth().signOut();
};
useEffect(() => {
const abortController = new AbortController();
firebase.auth().onAuthStateChanged(async _user => {
if (!_user) {
history.push('/login');
} else {
await firebase
.firestore()
.collection('chats')
.where('users', 'array-contains', _user.email)
.onSnapshot(async res => …Run Code Online (Sandbox Code Playgroud) async-await firebase reactjs google-cloud-firestore use-effect
我正在研究一个基于对象键管理字符串数组的函数。假设它看起来像这样:
import React, { useState, useEffect } from "react";
import FieldContext from "../contexts/FieldContext";
import io from "socket.io-client";
const [socket, setSocket] = useState(null);
// the `data` array gets changed every second due to a WebSocket, in case that's important
const [data, setData] = useState({ foo: [], bar: [] });
const [connections, setConnections] = useState(["conn1", "conn2"]);
const { checkedFields } = useContext(FieldContext); // ["foo", "moo"];
useEffect(() => {
setConnections(prevConnections => {
// The code below does the following:
// Loop through the …Run Code Online (Sandbox Code Playgroud) export function useShape (){
const [state, setState] = useState({
shape: 'square',
size: {
width: 100,
height: 100
}
})
// Change shape name while size update
useEffect(()=>{
const {size: {width, height}} = state
setState({
...state,
shape: width===height ? 'square' : 'rect'
})
}, [state, state.size])
}
Run Code Online (Sandbox Code Playgroud)
当尺寸更新时,副作用将根据宽度、高度更改尺寸名称。
目标是使状态保持一致,因此无论大小如何变化,我都将始终获得正确的形状。
useEffect 函数进入了一个循环,如果我删除了“状态”依赖会很好,但是智能感知请求“状态”依赖,那么解决方案是什么?
我有一个简单的组件,可以将链接复制到剪贴板,并且想用复选标记交换链接图标。我有这样做的逻辑设置,但是在 3 秒后重置状态以将按钮重置回链接图标时遇到问题。如何正确设置我的useEffect和状态钩子以设置然后重置状态以显示/隐藏选中标记的链接并再次返回?
const [copySuccess, setCopySuccess] = useState('');
const [visible, setVisible] = useState(true);
const copyToClipBoard = async copyHeader => {
try {
await navigator.clipboard.writeText(copyHeader);
setCopySuccess('Copied!');
} catch (err) {
setCopySuccess('Failed to copy!');
}
};
<Button>
{copySuccess ? (
<Icon name="success" />
):(
<Icon
name="linked"
onClick={() => copyToClipBoard(url)}
/>
)}
</Button>
Run Code Online (Sandbox Code Playgroud)
我正在尝试useEffect这样的:
useEffect(() => {
setTimeout(() => {
setVisible(false);
}, 3000);
});
Run Code Online (Sandbox Code Playgroud)
但不确定如何使用setVisible状态和超时,将图标交换回链接,让用户知道他们可以再次复制它。
useLayoutEffectvs 的一个不同之useEffect处在于useLayoutEffect它将在 DOM 突变之后和浏览器绘制阶段之前同步触发。(参考)
但是,我遇到了一些文章,它们似乎都建议useLayoutEffect用于阅读布局。
签名与 useEffect 相同,但在所有 DOM 突变后同步触发。使用它从 DOM 读取布局并同步重新渲染。在浏览器有机会绘制之前, useLayoutEffect 内计划的更新将同步刷新。
useLayoutEffect:如果您需要改变 DOM 和/或确实需要执行测量。
在可能的情况下,如果您希望在浏览器绘制过程中不显眼,请选择 useEffect Hook。在现实世界中,这通常是大多数时候!好吧,除非你从 DOM 读取布局或者做一些需要尽快绘制的与 DOM 相关的事情。
据我了解,在useEffect触发时,浏览器已经通过了布局和绘制阶段,因此读取布局useEffect应该完全没问题。
所以我的问题是:阅读布局时useLayoutEffect更可取useEffect,如果是,为什么?考虑到我只是阅读了布局,并没有在效果中执行任何额外的状态更改。
如果我有一个变量,其值可以根据另一个属性的值完全派生,那么初始化计算变量与使用useState/的组合useEffect来跟踪变量是否有任何后果/陷阱?让我用一个人为的例子来说明:
/**
* ex paymentAmounts: [100, 300, 400]
*/
const Option1 = ({paymentAmounts}) => {
const [average, setAverage] = useState(paymentAmounts.reduce((acc, curr) => curr + acc, 0) / paymentAmounts.length)
useEffect(() => {
setAverage(paymentAmounts.reduce((acc, curr) => curr + acc, 0) / paymentAmounts.length)
}, [paymentAmounts])
return (
<div>
Average: {average}
</div>
)
}
Run Code Online (Sandbox Code Playgroud)
或者更简单地说
/**
* ex paymentAmounts: [100, 300, 400]
*/
const Option2 = ({paymentAmounts}) => {
const average = paymentAmounts.reduce((acc, curr) => curr + acc, 0) / …Run Code Online (Sandbox Code Playgroud) 对 React 来说相对较新,遇到了一些我不确定答案的问题。如果有条件操作,useEffect与仅使用 if 语句有区别吗?
function Items() {
[setItemAdded, itemAdded] = useState(false);
if (itemAdded) {
saveData();
itemAdded = false;
}
}
Run Code Online (Sandbox Code Playgroud)
与
useEffect(() => {
if (itemAdded) {
saveData();
itemAdded = false;
}, [itemAdded]);
Run Code Online (Sandbox Code Playgroud)
根据我的理解,当状态更新时itemAdded,它将重新渲染将调用条件语句并执行代码的组件。
在第二种情况下,由于是一个依赖项,因此每当更改itemAdded时都会调用它。itemAdded对我来说,这两种情况似乎具有相同的效果。两者有区别吗?
reactjs ×10
use-effect ×10
react-hooks ×8
javascript ×4
use-state ×3
async-await ×1
firebase ×1
typescript ×1