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
嗨,我有一个场景,我需要在特定场景中使用 React_hooks 在反应钩子中设置新状态后替换 setState 回调。
当前代码是::
const ThemeOptions = () => {
const [previewType, setPreviewType] = useState("Desktop");
const [previewUrl, setPreviewUrl] = useState("");
const [layout, setLayout] = useState(null);
const [saving, setSaving] = useState(false);
const handleSave = (newdata) => {
setLayout(newdata);
setSaving(true);
axios
.put(window.urls.save, this.state.layout)
.then(
function(response) { // i want this to change in hooks
this.setState(
{
layout: response.data,
saving: false,
},
function() {
this.refs["preview"].refresh();
}
);
}.bind(this)
)
.catch(
function(error) {
console.log(error);
setSaving(false);
}.bind(this)
);
}
return (
<div>
<Router> …Run Code Online (Sandbox Code Playgroud) 我正在使用 Promise 进行多次调用。
我要获取的 API 端点是:
看代码
export function getTeamsStats(league, team, type) {
return function(dispatch) {
const url = "https://www.api-football.com/demo/v2/statistics";
let dates = ["2019-08-30", "2019-09-30", "2019-10-30"];
const getAllData = (dates, i) => {
return Promise.allSettled(dates.map(x => url + '/' + 357 + '/' + 5 + '/' + x).map(fetchData));
}
const fetchData = (URL) => {
return axios
.get(URL)
.then(res => {
const {
matchsPlayed: { total: teamsTotalMatchsPlayed},
} = res.data.api.statistics.matchs;
const matchsPlayed = teamsTotalMatchsPlayed;
dispatch(receivedTeamsStat(matchsPlayed, type)); …Run Code Online (Sandbox Code Playgroud) 我是新来的反应。我已经阅读了 React 文档。我不知道为什么它不起作用。所以,我来到这里。
我正在尝试在反应中创建分页。下面是我的table组件。
const Table = (props) => {
const { description, itemCount, tableHeaders, items } = props;
const pageSize = 5;
const [currentPage, setCurrentPage] = useState(1);
function handlePageChange(page) {
setCurrentPage(page);
}
const registerations = paginate(items, currentPage, pageSize);
// HERE: registerations data is correct and then I pass it to TableBody component.
return (
<React.Fragment>
<TableDescription description={description} count={itemCount} />
<div className="bg-white block w-full md:table">
<TableHeader items={tableHeaders} />
<TableBody items={registerations} />
</div>
{/* Footer & Pagination */}
<div …Run Code Online (Sandbox Code Playgroud) 我正在研究一个基于对象键管理字符串数组的函数。假设它看起来像这样:
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) 我已经使用 hooks 一段时间了,但我从来没有完全理解为什么 React 强迫我在 useEffect 上包含一些我不想要的依赖项。
\n我理解 useEffect 挂钩的“依赖关系”的方式
\n添加您想要在更改时“监听”的值并触发您的效果。这与简单的效果完美配合,例如:
\nimport React, {useEffect, useState} from "react";\n\ninterface Props {\n\xc2\xa0 \xc2\xa0 id: string\n}\n\nconst SimpleComponent = (props: Props) => {\n\xc2\xa0 \xc2\xa0 const {id} = props;\n\xc2\xa0 \xc2\xa0 const [response, setResponse] = useState<object>();\n\xc2\xa0 \xc2\xa0 \n\xc2\xa0 \xc2\xa0 useEffect(() => {\n\xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 fetch(`https://myexample/${id}`)\n\xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 .then(response => setResponse(response))\n\xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 .catch(() => console.log("An error occurs!"))\n\xc2\xa0 \xc2\xa0 }, [id])\n\xc2\xa0 \xc2\xa0 \n\xc2\xa0 \xc2\xa0 return <div/>\n};\nRun Code Online (Sandbox Code Playgroud)\n然而,还有一些其他情况并不像上面的例子那么简单。在这个例子中我们希望仅当 …
我有一个简单的组件,可以将链接复制到剪贴板,并且想用复选标记交换链接图标。我有这样做的逻辑设置,但是在 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,如果是,为什么?考虑到我只是阅读了布局,并没有在效果中执行任何额外的状态更改。
reactjs ×10
use-effect ×10
react-hooks ×9
javascript ×5
use-state ×3
async-await ×1
firebase ×1
promise ×1
setstate ×1