diff --git a/components/excalidraw/state-machine.excalidraw b/components/excalidraw/state-machine.excalidraw
new file mode 100644
index 00000000..95e7a8b3
--- /dev/null
+++ b/components/excalidraw/state-machine.excalidraw
@@ -0,0 +1,5744 @@
+{
+ "type": "excalidraw",
+ "version": 2,
+ "source": "https://excalidraw.com",
+ "elements": [
+ {
+ "id": "Kk0t3c3ntffk2sUfTlyEg",
+ "type": "rectangle",
+ "x": 613.0214904006891,
+ "y": 491.47238998082344,
+ "width": 1210.9077358360364,
+ "height": 418.33413843075,
+ "angle": 0,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "transparent",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "seed": 1509111820,
+ "version": 276,
+ "versionNonce": 1122045580,
+ "isDeleted": false,
+ "boundElements": null,
+ "updated": 1661872465013,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "rectangle",
+ "version": 238,
+ "versionNonce": 323341964,
+ "isDeleted": false,
+ "id": "YYeS3AhcleSaAiDoqlpSa",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 711,
+ "y": 743,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 173,
+ "height": 73.99999999999999,
+ "seed": 1770431273,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408317,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 179,
+ "versionNonce": 128790580,
+ "isDeleted": false,
+ "id": "Jb5OzMemcdtU0lQgAtrT2",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 751,
+ "y": 765.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 90,
+ "height": 26,
+ "seed": 1934360967,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408317,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "undefined",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "undefined"
+ },
+ {
+ "type": "rectangle",
+ "version": 326,
+ "versionNonce": 2032409868,
+ "isDeleted": false,
+ "id": "1J-znl1gWOl4DgpMVqy46",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 709.5,
+ "y": 661,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "#fab005",
+ "width": 173,
+ "height": 56,
+ "seed": 1406154791,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "type": "text",
+ "id": "w2QmI_TLGeChUqREIieDB"
+ }
+ ],
+ "updated": 1661872408317,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 100,
+ "versionNonce": 813762996,
+ "isDeleted": false,
+ "id": "w2QmI_TLGeChUqREIieDB",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 714.5,
+ "y": 677,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 1552158887,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408317,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isValidating",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "1J-znl1gWOl4DgpMVqy46",
+ "originalText": "isValidating"
+ },
+ {
+ "type": "rectangle",
+ "version": 378,
+ "versionNonce": 1530174348,
+ "isDeleted": false,
+ "id": "LVh4uVLrroMXen1Kdp2lx",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 709.5,
+ "y": 589,
+ "strokeColor": "#1864ab",
+ "backgroundColor": "#15aabf",
+ "width": 173,
+ "height": 42,
+ "seed": 1275784745,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "rN1Yi8_oIcZyW2tZHVXY5",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "rN1Yi8_oIcZyW2tZHVXY5"
+ }
+ ],
+ "updated": 1661872408317,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 159,
+ "versionNonce": 345879348,
+ "isDeleted": false,
+ "id": "rN1Yi8_oIcZyW2tZHVXY5",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 714.5,
+ "y": 598,
+ "strokeColor": "#1864ab",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 16852199,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408317,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isLoading",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "LVh4uVLrroMXen1Kdp2lx",
+ "originalText": "isLoading"
+ },
+ {
+ "type": "arrow",
+ "version": 149,
+ "versionNonce": 1404127756,
+ "isDeleted": false,
+ "id": "1m5JBt1t2mfGAhRS9bkZm",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 715,
+ "y": 852,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 164,
+ "height": 2,
+ "seed": 2056928583,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408317,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 164,
+ 2
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 99,
+ "versionNonce": 1919495348,
+ "isDeleted": false,
+ "id": "TqxbxrMn8PwdIQMEM5KRS",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 752,
+ "y": 860,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 77,
+ "height": 26,
+ "seed": 583218951,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408317,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "fetching",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "fetching"
+ },
+ {
+ "type": "rectangle",
+ "version": 459,
+ "versionNonce": 630466700,
+ "isDeleted": false,
+ "id": "KImgLwVzg-Tu0JxYS-JVA",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 937.5,
+ "y": 744,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 239.00000000000006,
+ "height": 73.00000000000001,
+ "seed": 1737661351,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408317,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 296,
+ "versionNonce": 1267299892,
+ "isDeleted": false,
+ "id": "ip_A8a3IKow9H2qF6O2nn",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1032,
+ "y": 764.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 52,
+ "height": 26,
+ "seed": 1166026407,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408317,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "value",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "value"
+ },
+ {
+ "type": "arrow",
+ "version": 283,
+ "versionNonce": 613889804,
+ "isDeleted": false,
+ "id": "ds60SG5KbMPUqEwqog7-n",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1017,
+ "y": 852,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 149,
+ "height": 2,
+ "seed": 343575591,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 149,
+ 2
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 122,
+ "versionNonce": 2095353780,
+ "isDeleted": false,
+ "id": "HiuNh4LiXoo948aNvUFaJ",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1050.5,
+ "y": 860,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 77,
+ "height": 26,
+ "seed": 962136199,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "fetching",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "fetching"
+ },
+ {
+ "type": "rectangle",
+ "version": 372,
+ "versionNonce": 489977228,
+ "isDeleted": false,
+ "id": "q1JH4SvIeawtiKQdjMRcC",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1004.5,
+ "y": 662,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "#fab005",
+ "width": 173,
+ "height": 55,
+ "seed": 318729671,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "6zf3U1HXWu_TAFbGG3k3I",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "6zf3U1HXWu_TAFbGG3k3I"
+ }
+ ],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 145,
+ "versionNonce": 1453357364,
+ "isDeleted": false,
+ "id": "6zf3U1HXWu_TAFbGG3k3I",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1009.5,
+ "y": 677.5,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 827094569,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isValidating",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "q1JH4SvIeawtiKQdjMRcC",
+ "originalText": "isValidating"
+ },
+ {
+ "type": "rectangle",
+ "version": 238,
+ "versionNonce": 1017504780,
+ "isDeleted": false,
+ "id": "AbyTVn94-z0aAlq0rznNB",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1218.5,
+ "y": 745,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 173,
+ "height": 71,
+ "seed": 1085310663,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 275,
+ "versionNonce": 874721972,
+ "isDeleted": false,
+ "id": "lQmEro2yWiEj_Dhr2LaZ6",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1261,
+ "y": 764.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 95,
+ "height": 26,
+ "seed": 385973033,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "new value",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "new value"
+ },
+ {
+ "type": "rectangle",
+ "version": 352,
+ "versionNonce": 2028307084,
+ "isDeleted": false,
+ "id": "kq7aAMkOKrKh1yOMU_ytI",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 699.5,
+ "y": 1235,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 173,
+ "height": 73.99999999999999,
+ "seed": 395060489,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 297,
+ "versionNonce": 336772148,
+ "isDeleted": false,
+ "id": "MiC3ayLU7UnLHG7PetCVF",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 739.5,
+ "y": 1256.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 90,
+ "height": 26,
+ "seed": 310070279,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "undefined",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "undefined"
+ },
+ {
+ "type": "rectangle",
+ "version": 442,
+ "versionNonce": 2024624396,
+ "isDeleted": false,
+ "id": "wksvA5_uNH119ChV85tC8",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 698,
+ "y": 1153,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "#fab005",
+ "width": 173,
+ "height": 56,
+ "seed": 1588894697,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "b1IgQquu9tLyTntgliMam",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "b1IgQquu9tLyTntgliMam"
+ }
+ ],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 215,
+ "versionNonce": 810882484,
+ "isDeleted": false,
+ "id": "b1IgQquu9tLyTntgliMam",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 703,
+ "y": 1169,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 157430567,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isValidating",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "wksvA5_uNH119ChV85tC8",
+ "originalText": "isValidating"
+ },
+ {
+ "type": "rectangle",
+ "version": 494,
+ "versionNonce": 2071416716,
+ "isDeleted": false,
+ "id": "px-PDxzOhcDjkMUaOaVI2",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 698,
+ "y": 1081,
+ "strokeColor": "#1864ab",
+ "backgroundColor": "#15aabf",
+ "width": 173,
+ "height": 42,
+ "seed": 1533753033,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "MbGbH1tVVt8K0oFevrXmw",
+ "type": "text"
+ },
+ {
+ "id": "MbGbH1tVVt8K0oFevrXmw",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "MbGbH1tVVt8K0oFevrXmw"
+ }
+ ],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 274,
+ "versionNonce": 370163508,
+ "isDeleted": false,
+ "id": "MbGbH1tVVt8K0oFevrXmw",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 703,
+ "y": 1090,
+ "strokeColor": "#1864ab",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 1293891143,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isLoading",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "px-PDxzOhcDjkMUaOaVI2",
+ "originalText": "isLoading"
+ },
+ {
+ "type": "arrow",
+ "version": 263,
+ "versionNonce": 1098743308,
+ "isDeleted": false,
+ "id": "W3F1uAVoPCCSzF6oUC4sO",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 703.5,
+ "y": 1344,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 164,
+ "height": 2,
+ "seed": 4945321,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 164,
+ 2
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 196,
+ "versionNonce": 1034565812,
+ "isDeleted": false,
+ "id": "Jx4l2GwA-1hH8zOh07y8T",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 736.5,
+ "y": 1358,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 77,
+ "height": 26,
+ "seed": 344905063,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "fetching",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "fetching"
+ },
+ {
+ "type": "rectangle",
+ "version": 642,
+ "versionNonce": 1941560460,
+ "isDeleted": false,
+ "id": "x22YcQUfDJ9HsvjkiX5Yn",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 905,
+ "y": 1234,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 173.00000000000006,
+ "height": 73.00000000000001,
+ "seed": 1619056777,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 434,
+ "versionNonce": 583598644,
+ "isDeleted": false,
+ "id": "p0PPTIZt7aeVURBn0dGfS",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 956.5,
+ "y": 1256.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 73,
+ "height": 26,
+ "seed": 117280903,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "value(1)",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "value(1)"
+ },
+ {
+ "type": "arrow",
+ "version": 501,
+ "versionNonce": 199650060,
+ "isDeleted": false,
+ "id": "Iw_sUfAFmcm74gT3C3nD0",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1129.5,
+ "y": 1332,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 168,
+ "height": 2,
+ "seed": 499572585,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 168,
+ 2
+ ]
+ ]
+ },
+ {
+ "type": "rectangle",
+ "version": 645,
+ "versionNonce": 1548917684,
+ "isDeleted": false,
+ "id": "xdVRpOKF4Gn8JIouGIW-m",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1412,
+ "y": 1147,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "#fab005",
+ "width": 173,
+ "height": 55,
+ "seed": 375982665,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "XkVkYu-734HjBFYduM1fW",
+ "type": "text"
+ },
+ {
+ "id": "XkVkYu-734HjBFYduM1fW",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "XkVkYu-734HjBFYduM1fW"
+ }
+ ],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 417,
+ "versionNonce": 1014835596,
+ "isDeleted": false,
+ "id": "XkVkYu-734HjBFYduM1fW",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1417,
+ "y": 1162.5,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 674912967,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isValidating",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "xdVRpOKF4Gn8JIouGIW-m",
+ "originalText": "isValidating"
+ },
+ {
+ "type": "rectangle",
+ "version": 452,
+ "versionNonce": 879559988,
+ "isDeleted": false,
+ "id": "P2bKLC1WnDg2IUxEzGPPl",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1127,
+ "y": 1235,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 172.00000000000006,
+ "height": 71,
+ "seed": 1396889897,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 159,
+ "versionNonce": 1791976716,
+ "isDeleted": false,
+ "id": "pvFyKnPV8G4wQhIrIH0_N",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 626.9412738941411,
+ "y": 510.2162031605824,
+ "strokeColor": "#000000",
+ "backgroundColor": "#15aabf",
+ "width": 214,
+ "height": 26,
+ "seed": 1760771143,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872486424,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Fetch and Revalidate",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Fetch and Revalidate"
+ },
+ {
+ "type": "text",
+ "version": 221,
+ "versionNonce": 995857420,
+ "isDeleted": false,
+ "id": "PRvyVsyGooeZB3HyIJ1aD",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 642.3151956386723,
+ "y": 999.7837968394175,
+ "strokeColor": "#000000",
+ "backgroundColor": "#15aabf",
+ "width": 110,
+ "height": 26,
+ "seed": 955668295,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872490947,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Key Change",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Key Change"
+ },
+ {
+ "type": "line",
+ "version": 123,
+ "versionNonce": 1016149644,
+ "isDeleted": false,
+ "id": "1vQcpsTi6BSuNVdH9YEKr",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1085,
+ "y": 1075,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "#15aabf",
+ "width": 3,
+ "height": 289,
+ "seed": 191323751,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": null,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 3,
+ 289
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 352,
+ "versionNonce": 2032945204,
+ "isDeleted": false,
+ "id": "RL4TzbmCNDEOw8u51p9qo",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1032.5,
+ "y": 1034,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "#15aabf",
+ "width": 110,
+ "height": 26,
+ "seed": 1971744905,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Key Change",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Key Change"
+ },
+ {
+ "type": "text",
+ "version": 498,
+ "versionNonce": 885163276,
+ "isDeleted": false,
+ "id": "WpG9TPQ4O6-haeBwowQUa",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1168.5,
+ "y": 1256.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 90,
+ "height": 26,
+ "seed": 1549951401,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "undefined",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "undefined"
+ },
+ {
+ "type": "rectangle",
+ "version": 487,
+ "versionNonce": 1381835188,
+ "isDeleted": false,
+ "id": "JfzoPwS4vrEVfP-eTFfW9",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1126.5,
+ "y": 1146,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "#fab005",
+ "width": 173,
+ "height": 56,
+ "seed": 1218507559,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "Nmu8TOtd3R_6mRmlKHAfx",
+ "type": "text"
+ },
+ {
+ "id": "Nmu8TOtd3R_6mRmlKHAfx",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "Nmu8TOtd3R_6mRmlKHAfx"
+ }
+ ],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 259,
+ "versionNonce": 1724253068,
+ "isDeleted": false,
+ "id": "Nmu8TOtd3R_6mRmlKHAfx",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1131.5,
+ "y": 1162,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 1148745417,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isValidating",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "JfzoPwS4vrEVfP-eTFfW9",
+ "originalText": "isValidating"
+ },
+ {
+ "type": "rectangle",
+ "version": 554,
+ "versionNonce": 1396099892,
+ "isDeleted": false,
+ "id": "3YeTivkKa5hsNUtTBvbqK",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1123.5,
+ "y": 1074,
+ "strokeColor": "#1864ab",
+ "backgroundColor": "#15aabf",
+ "width": 173,
+ "height": 42,
+ "seed": 1720078089,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "nXkvKp4ucvpYgd6I-Dm6h",
+ "type": "text"
+ },
+ {
+ "id": "nXkvKp4ucvpYgd6I-Dm6h",
+ "type": "text"
+ },
+ {
+ "id": "nXkvKp4ucvpYgd6I-Dm6h",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "nXkvKp4ucvpYgd6I-Dm6h"
+ }
+ ],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 333,
+ "versionNonce": 953528844,
+ "isDeleted": false,
+ "id": "nXkvKp4ucvpYgd6I-Dm6h",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1128.5,
+ "y": 1083,
+ "strokeColor": "#1864ab",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 1729151495,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isLoading",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "3YeTivkKa5hsNUtTBvbqK",
+ "originalText": "isLoading"
+ },
+ {
+ "type": "text",
+ "version": 278,
+ "versionNonce": 773801140,
+ "isDeleted": false,
+ "id": "pQKV4tvHQLgJWJk75289t",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1178.5,
+ "y": 1352,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 77,
+ "height": 26,
+ "seed": 1909728615,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "fetching",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "fetching"
+ },
+ {
+ "type": "rectangle",
+ "version": 580,
+ "versionNonce": 971739276,
+ "isDeleted": false,
+ "id": "9HKgXaXUQ8bNQKIwUbH2R",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1339,
+ "y": 1230.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 254,
+ "height": 71,
+ "seed": 831128711,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 582,
+ "versionNonce": 1488752180,
+ "isDeleted": false,
+ "id": "QiCyaHLuzJYIr5yrJjAHE",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1429,
+ "y": 1254.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 82,
+ "height": 26,
+ "seed": 439704329,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "value(2)",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "value(2)"
+ },
+ {
+ "type": "line",
+ "version": 335,
+ "versionNonce": 163684108,
+ "isDeleted": false,
+ "id": "w7vwwL-nYi1FCr_DnOXtt",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 993.5,
+ "y": 574.5,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "#15aabf",
+ "width": 4,
+ "height": 158,
+ "seed": 151844873,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": null,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 4,
+ 158
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 427,
+ "versionNonce": 603236276,
+ "isDeleted": false,
+ "id": "qS9CXouucCnVaWBbxOKCl",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 946,
+ "y": 548,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "#15aabf",
+ "width": 106,
+ "height": 26,
+ "seed": 894509703,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Revalidate",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Revalidate"
+ },
+ {
+ "type": "line",
+ "version": 452,
+ "versionNonce": 1087495564,
+ "isDeleted": false,
+ "id": "1OI0D9FUkslc0jsno07Ja",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1404,
+ "y": 1068,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "#15aabf",
+ "width": 0,
+ "height": 147,
+ "seed": 850202697,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": null,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 147
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 466,
+ "versionNonce": 1408078132,
+ "isDeleted": false,
+ "id": "pi2UVgs40tFhpPqDF2obd",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1343,
+ "y": 1031,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "#15aabf",
+ "width": 106,
+ "height": 26,
+ "seed": 677324039,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Revalidate",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Revalidate"
+ },
+ {
+ "type": "rectangle",
+ "version": 500,
+ "versionNonce": 1482050572,
+ "isDeleted": false,
+ "id": "UAUUIDK6zZlcVWEgN2qE7",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1617,
+ "y": 1229.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 172.00000000000006,
+ "height": 71,
+ "seed": 1672240807,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 581,
+ "versionNonce": 940395188,
+ "isDeleted": false,
+ "id": "umRTpRpHJ1xzYvcR3VDZf",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1648,
+ "y": 1251.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 124,
+ "height": 26,
+ "seed": 1162524745,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "new value(2)",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "new value(2)"
+ },
+ {
+ "type": "text",
+ "version": 220,
+ "versionNonce": 306768524,
+ "isDeleted": false,
+ "id": "jDwsdJ6VGhZC08ivh5mf_",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 631,
+ "y": 767.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 55,
+ "height": 26,
+ "seed": 1934804007,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "data:",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "data:"
+ },
+ {
+ "type": "text",
+ "version": 283,
+ "versionNonce": 1917080884,
+ "isDeleted": false,
+ "id": "i5DdpjlG8SyLtpwaoc-3s",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 630.1982264854048,
+ "y": 1265,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 55,
+ "height": 26,
+ "seed": 944651369,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872496914,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "data:",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "data:"
+ },
+ {
+ "type": "rectangle",
+ "version": 407,
+ "versionNonce": 1633925388,
+ "isDeleted": false,
+ "id": "X07576UrkZKyxe1jE1j4e",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 696.25,
+ "y": 1721.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 173,
+ "height": 73.99999999999999,
+ "seed": 962185321,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 352,
+ "versionNonce": 580464052,
+ "isDeleted": false,
+ "id": "i_q6mz5TIxb-M329b8FIO",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 736.25,
+ "y": 1743,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 90,
+ "height": 26,
+ "seed": 2018774695,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "undefined",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "undefined"
+ },
+ {
+ "type": "rectangle",
+ "version": 499,
+ "versionNonce": 1361098636,
+ "isDeleted": false,
+ "id": "X4JgKjaJLwbt6gnY1kQ-b",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 694.75,
+ "y": 1639.5,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "#fab005",
+ "width": 173,
+ "height": 56,
+ "seed": 1907807049,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "gSUd6JtRSlsBmQDkZegv6",
+ "type": "text"
+ },
+ {
+ "id": "gSUd6JtRSlsBmQDkZegv6",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "gSUd6JtRSlsBmQDkZegv6"
+ }
+ ],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 271,
+ "versionNonce": 1477531444,
+ "isDeleted": false,
+ "id": "gSUd6JtRSlsBmQDkZegv6",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 699.75,
+ "y": 1655.5,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 1212785095,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isValidating",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "X4JgKjaJLwbt6gnY1kQ-b",
+ "originalText": "isValidating"
+ },
+ {
+ "type": "rectangle",
+ "version": 551,
+ "versionNonce": 1840899596,
+ "isDeleted": false,
+ "id": "iIvhSb1OKvlc0ZJS4ooP4",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 694.75,
+ "y": 1567.5,
+ "strokeColor": "#1864ab",
+ "backgroundColor": "#15aabf",
+ "width": 173,
+ "height": 42,
+ "seed": 1945076265,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "TI9JnvAAvgrqCqWSlBlRt",
+ "type": "text"
+ },
+ {
+ "id": "TI9JnvAAvgrqCqWSlBlRt",
+ "type": "text"
+ },
+ {
+ "id": "TI9JnvAAvgrqCqWSlBlRt",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "TI9JnvAAvgrqCqWSlBlRt"
+ }
+ ],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 330,
+ "versionNonce": 1338527924,
+ "isDeleted": false,
+ "id": "TI9JnvAAvgrqCqWSlBlRt",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 699.75,
+ "y": 1576.5,
+ "strokeColor": "#1864ab",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 643813607,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408318,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isLoading",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "iIvhSb1OKvlc0ZJS4ooP4",
+ "originalText": "isLoading"
+ },
+ {
+ "type": "arrow",
+ "version": 318,
+ "versionNonce": 740902028,
+ "isDeleted": false,
+ "id": "jUtmRAPJuXu2udxcePfL_",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 700.25,
+ "y": 1830.5,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 164,
+ "height": 2,
+ "seed": 1290753289,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 164,
+ 2
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 251,
+ "versionNonce": 2036603444,
+ "isDeleted": false,
+ "id": "YLre-hfIm8KeZeahU3FLV",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 733.25,
+ "y": 1844.5,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 77,
+ "height": 26,
+ "seed": 1008933895,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "fetching",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "fetching"
+ },
+ {
+ "type": "rectangle",
+ "version": 697,
+ "versionNonce": 1378407180,
+ "isDeleted": false,
+ "id": "InDDRYcfBfbQd1s2S2aF9",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 901.75,
+ "y": 1720.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 173.00000000000006,
+ "height": 73.00000000000001,
+ "seed": 1613876201,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 489,
+ "versionNonce": 1607119796,
+ "isDeleted": false,
+ "id": "5r5B3kuS9m_Iv9qBTvrFR",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 953.25,
+ "y": 1743,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 73,
+ "height": 26,
+ "seed": 1301283623,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "value(1)",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "value(1)"
+ },
+ {
+ "type": "arrow",
+ "version": 556,
+ "versionNonce": 1580372364,
+ "isDeleted": false,
+ "id": "QzAaFPZmBhKQhIVu8unk_",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1126.25,
+ "y": 1818.5,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 168,
+ "height": 2,
+ "seed": 365061833,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 168,
+ 2
+ ]
+ ]
+ },
+ {
+ "type": "rectangle",
+ "version": 702,
+ "versionNonce": 1092754740,
+ "isDeleted": false,
+ "id": "afO9NshUcZaVaF9KmE14S",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1408.75,
+ "y": 1633.5,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "#fab005",
+ "width": 173,
+ "height": 55,
+ "seed": 1796408903,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "3fjkrs14oR5ek-x38lZQr",
+ "type": "text"
+ },
+ {
+ "id": "3fjkrs14oR5ek-x38lZQr",
+ "type": "text"
+ },
+ {
+ "id": "3fjkrs14oR5ek-x38lZQr",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "3fjkrs14oR5ek-x38lZQr"
+ }
+ ],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 473,
+ "versionNonce": 1870692364,
+ "isDeleted": false,
+ "id": "3fjkrs14oR5ek-x38lZQr",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1413.75,
+ "y": 1649,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 1211934121,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isValidating",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "afO9NshUcZaVaF9KmE14S",
+ "originalText": "isValidating"
+ },
+ {
+ "type": "rectangle",
+ "version": 507,
+ "versionNonce": 651187892,
+ "isDeleted": false,
+ "id": "WPNszsjUgNwQhP316UaQJ",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1123.75,
+ "y": 1721.5,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 172.00000000000006,
+ "height": 71,
+ "seed": 1669899623,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 264,
+ "versionNonce": 1622964404,
+ "isDeleted": false,
+ "id": "aHfSChp40csLmQFOcY_Zs",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 623.745008232533,
+ "y": 1477.186418156195,
+ "strokeColor": "#000000",
+ "backgroundColor": "#15aabf",
+ "width": 287,
+ "height": 26,
+ "seed": 1925974153,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872539938,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Key Change + Previous Data",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Key Change + Previous Data"
+ },
+ {
+ "type": "line",
+ "version": 178,
+ "versionNonce": 2105237556,
+ "isDeleted": false,
+ "id": "lOE_vnVNQC9K5Se2bWWLx",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1081.75,
+ "y": 1561.5,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "#15aabf",
+ "width": 3,
+ "height": 289,
+ "seed": 1983733895,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": null,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 3,
+ 289
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 407,
+ "versionNonce": 848358668,
+ "isDeleted": false,
+ "id": "S6Bjn3t1PM__g66H_vo2r",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1029.25,
+ "y": 1520.5,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "#15aabf",
+ "width": 110,
+ "height": 26,
+ "seed": 522981225,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Key Change",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Key Change"
+ },
+ {
+ "type": "text",
+ "version": 561,
+ "versionNonce": 755579316,
+ "isDeleted": false,
+ "id": "jazia19SdkkqQe1VjY_Nb",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1165.25,
+ "y": 1743,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 73,
+ "height": 26,
+ "seed": 1156429735,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "value(1)",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "value(1)"
+ },
+ {
+ "type": "rectangle",
+ "version": 544,
+ "versionNonce": 1854368652,
+ "isDeleted": false,
+ "id": "iXoEcrLefuWQYmtqmztJU",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1123.25,
+ "y": 1632.5,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "#fab005",
+ "width": 173,
+ "height": 56,
+ "seed": 329792073,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "dMr5YFLxcV68cWumeHfRa",
+ "type": "text"
+ },
+ {
+ "id": "dMr5YFLxcV68cWumeHfRa",
+ "type": "text"
+ },
+ {
+ "id": "dMr5YFLxcV68cWumeHfRa",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "dMr5YFLxcV68cWumeHfRa"
+ }
+ ],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 315,
+ "versionNonce": 815784756,
+ "isDeleted": false,
+ "id": "dMr5YFLxcV68cWumeHfRa",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1128.25,
+ "y": 1648.5,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 96873159,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isValidating",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "iXoEcrLefuWQYmtqmztJU",
+ "originalText": "isValidating"
+ },
+ {
+ "type": "rectangle",
+ "version": 611,
+ "versionNonce": 338303500,
+ "isDeleted": false,
+ "id": "x1I3OXDFuU-xA23V3GLFK",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1120.25,
+ "y": 1560.5,
+ "strokeColor": "#1864ab",
+ "backgroundColor": "#15aabf",
+ "width": 173,
+ "height": 42,
+ "seed": 1092356393,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "TKz6NDSTv44S6Ap8IO6nL",
+ "type": "text"
+ },
+ {
+ "id": "TKz6NDSTv44S6Ap8IO6nL",
+ "type": "text"
+ },
+ {
+ "id": "TKz6NDSTv44S6Ap8IO6nL",
+ "type": "text"
+ },
+ {
+ "id": "TKz6NDSTv44S6Ap8IO6nL",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "TKz6NDSTv44S6Ap8IO6nL"
+ }
+ ],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 389,
+ "versionNonce": 782589108,
+ "isDeleted": false,
+ "id": "TKz6NDSTv44S6Ap8IO6nL",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1125.25,
+ "y": 1569.5,
+ "strokeColor": "#1864ab",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 1969437159,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isLoading",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "x1I3OXDFuU-xA23V3GLFK",
+ "originalText": "isLoading"
+ },
+ {
+ "type": "text",
+ "version": 333,
+ "versionNonce": 2121463948,
+ "isDeleted": false,
+ "id": "aDYiQzh8aBqpdn9i2IDAV",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1175.25,
+ "y": 1838.5,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 77,
+ "height": 26,
+ "seed": 1894972425,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "fetching",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "fetching"
+ },
+ {
+ "type": "rectangle",
+ "version": 635,
+ "versionNonce": 280995380,
+ "isDeleted": false,
+ "id": "6EcRzruvGhRl4qaQRvDWP",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1335.75,
+ "y": 1717,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 254,
+ "height": 71,
+ "seed": 147940615,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 637,
+ "versionNonce": 421267212,
+ "isDeleted": false,
+ "id": "Xj3uEM-lc8n4WhXQf-P_u",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1425.75,
+ "y": 1741,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 82,
+ "height": 26,
+ "seed": 858297065,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "value(2)",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "value(2)"
+ },
+ {
+ "type": "line",
+ "version": 507,
+ "versionNonce": 466927540,
+ "isDeleted": false,
+ "id": "EhwJhNvvM65Ful8w87Bii",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1400.75,
+ "y": 1554.5,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "#15aabf",
+ "width": 0,
+ "height": 147,
+ "seed": 1603087399,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": null,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 147
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 521,
+ "versionNonce": 1218397580,
+ "isDeleted": false,
+ "id": "lj_vDV4q0y3b0e2h-SFqH",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1339.75,
+ "y": 1517.5,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "#15aabf",
+ "width": 106,
+ "height": 26,
+ "seed": 206065097,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Revalidate",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Revalidate"
+ },
+ {
+ "type": "rectangle",
+ "version": 555,
+ "versionNonce": 134916404,
+ "isDeleted": false,
+ "id": "uncQ_kN0RPYwcqavzlqSa",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1613.75,
+ "y": 1716,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 172.00000000000006,
+ "height": 71,
+ "seed": 1971523399,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 636,
+ "versionNonce": 1379316748,
+ "isDeleted": false,
+ "id": "Mq_1_Id6358tb36qOUxZO",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1644.75,
+ "y": 1738,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 124,
+ "height": 26,
+ "seed": 1668132009,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "new value(2)",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "new value(2)"
+ },
+ {
+ "type": "text",
+ "version": 353,
+ "versionNonce": 558004108,
+ "isDeleted": false,
+ "id": "PylRCXiz6gf90EYur2T2Z",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 622.6611182971225,
+ "y": 1749.186418156195,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 55,
+ "height": 26,
+ "seed": 353341031,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872543280,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "data:",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "data:"
+ },
+ {
+ "type": "rectangle",
+ "version": 310,
+ "versionNonce": 1138512524,
+ "isDeleted": false,
+ "id": "ihyAzvaNiz5uA8GaiLPWW",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 686.8884595539555,
+ "y": 2173.189559258307,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 173,
+ "height": 73.99999999999999,
+ "seed": 509834857,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 259,
+ "versionNonce": 884010036,
+ "isDeleted": false,
+ "id": "T6j3IFZiAn7Hb9It95qsZ",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 726.8884595539555,
+ "y": 2195.689559258307,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 78,
+ "height": 26,
+ "seed": 75776167,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "fallback",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "fallback"
+ },
+ {
+ "type": "rectangle",
+ "version": 400,
+ "versionNonce": 1548361996,
+ "isDeleted": false,
+ "id": "zJBnW06vdcbbfBRnSc108",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 685.3884595539555,
+ "y": 2091.189559258307,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "#fab005",
+ "width": 173,
+ "height": 56,
+ "seed": 626704713,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "smpycngSOyIJhmm0gXd-I",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "smpycngSOyIJhmm0gXd-I"
+ }
+ ],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 173,
+ "versionNonce": 2108507572,
+ "isDeleted": false,
+ "id": "smpycngSOyIJhmm0gXd-I",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 690.3884595539555,
+ "y": 2107.189559258307,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 61051847,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isValidating",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "zJBnW06vdcbbfBRnSc108",
+ "originalText": "isValidating"
+ },
+ {
+ "type": "rectangle",
+ "version": 452,
+ "versionNonce": 1928994700,
+ "isDeleted": false,
+ "id": "H3Zl2XwzzExidUig8HVNH",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 685.3884595539555,
+ "y": 2019.1895592583069,
+ "strokeColor": "#1864ab",
+ "backgroundColor": "#15aabf",
+ "width": 173,
+ "height": 42,
+ "seed": 686141481,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "xpZbDk2X0FMZescwVomsC",
+ "type": "text"
+ },
+ {
+ "id": "xpZbDk2X0FMZescwVomsC",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "xpZbDk2X0FMZescwVomsC"
+ }
+ ],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 232,
+ "versionNonce": 1759670068,
+ "isDeleted": false,
+ "id": "xpZbDk2X0FMZescwVomsC",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 690.3884595539555,
+ "y": 2028.1895592583069,
+ "strokeColor": "#1864ab",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 95126247,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isLoading",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "H3Zl2XwzzExidUig8HVNH",
+ "originalText": "isLoading"
+ },
+ {
+ "type": "arrow",
+ "version": 221,
+ "versionNonce": 1613123084,
+ "isDeleted": false,
+ "id": "xS9IIQDLkgErZQzqQ0fhH",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 690.8884595539555,
+ "y": 2282.189559258307,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 164,
+ "height": 2,
+ "seed": 519029513,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 164,
+ 2
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 171,
+ "versionNonce": 1454794932,
+ "isDeleted": false,
+ "id": "NxWUY18f8e9lP2XEM6Gc7",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 727.8884595539555,
+ "y": 2290.189559258307,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 77,
+ "height": 26,
+ "seed": 1529384455,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "fetching",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "fetching"
+ },
+ {
+ "type": "rectangle",
+ "version": 531,
+ "versionNonce": 1690872972,
+ "isDeleted": false,
+ "id": "eOUmTf_pl0jPtRAyMJSos",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 913.3884595539555,
+ "y": 2174.189559258307,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 239.00000000000006,
+ "height": 73.00000000000001,
+ "seed": 871660009,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 368,
+ "versionNonce": 718221876,
+ "isDeleted": false,
+ "id": "QlctkAze-M5eHvPo6B-Lc",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1007.8884595539555,
+ "y": 2194.689559258307,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 52,
+ "height": 26,
+ "seed": 253179175,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "value",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "value"
+ },
+ {
+ "type": "arrow",
+ "version": 355,
+ "versionNonce": 312163084,
+ "isDeleted": false,
+ "id": "GxwgVKF1EMfg57zGxAjys",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 992.8884595539555,
+ "y": 2282.189559258307,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 149,
+ "height": 2,
+ "seed": 2029999305,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 149,
+ 2
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 194,
+ "versionNonce": 1678953396,
+ "isDeleted": false,
+ "id": "WhtuZUvyw2pY3WuNnesmR",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1026.3884595539555,
+ "y": 2290.189559258307,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 77,
+ "height": 26,
+ "seed": 417593415,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "fetching",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "fetching"
+ },
+ {
+ "type": "rectangle",
+ "version": 446,
+ "versionNonce": 873986444,
+ "isDeleted": false,
+ "id": "FcylyWM7Qd_0-_dy9wM8R",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 980.3884595539555,
+ "y": 2092.189559258307,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "#fab005",
+ "width": 173,
+ "height": 55,
+ "seed": 1370215337,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "6TqaFXsbq8mVtLoqd-cjM",
+ "type": "text"
+ },
+ {
+ "id": "6TqaFXsbq8mVtLoqd-cjM",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "6TqaFXsbq8mVtLoqd-cjM"
+ }
+ ],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 218,
+ "versionNonce": 883373364,
+ "isDeleted": false,
+ "id": "6TqaFXsbq8mVtLoqd-cjM",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 985.3884595539555,
+ "y": 2107.689559258307,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 1315657575,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isValidating",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "FcylyWM7Qd_0-_dy9wM8R",
+ "originalText": "isValidating"
+ },
+ {
+ "type": "rectangle",
+ "version": 310,
+ "versionNonce": 880338956,
+ "isDeleted": false,
+ "id": "JShdMRiNe0cY7wZNfkImX",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1194.3884595539555,
+ "y": 2175.189559258307,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 173,
+ "height": 71,
+ "seed": 572880521,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 347,
+ "versionNonce": 435318452,
+ "isDeleted": false,
+ "id": "IFv2LoPJw-Q4vfKzEibw2",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1236.8884595539555,
+ "y": 2194.689559258307,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 95,
+ "height": 26,
+ "seed": 368693895,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "new value",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "new value"
+ },
+ {
+ "type": "text",
+ "version": 222,
+ "versionNonce": 2020549004,
+ "isDeleted": false,
+ "id": "xNer53OL1VYGexoBT2N17",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 626.2649588493185,
+ "y": 1939.0920228049895,
+ "strokeColor": "#000000",
+ "backgroundColor": "#15aabf",
+ "width": 80,
+ "height": 26,
+ "seed": 1026014569,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872556299,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Fallback",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Fallback"
+ },
+ {
+ "type": "line",
+ "version": 407,
+ "versionNonce": 1956280372,
+ "isDeleted": false,
+ "id": "93Pb9UyBoNwG41sc0hr-n",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 969.3884595539555,
+ "y": 2004.6895592583069,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "#15aabf",
+ "width": 4,
+ "height": 158,
+ "seed": 1481370023,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408319,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": null,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 4,
+ 158
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 499,
+ "versionNonce": 311383308,
+ "isDeleted": false,
+ "id": "hZO5JKafA3T20N9oQvGbg",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 921.8884595539555,
+ "y": 1978.1895592583069,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "#15aabf",
+ "width": 106,
+ "height": 26,
+ "seed": 301869129,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Revalidate",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Revalidate"
+ },
+ {
+ "type": "text",
+ "version": 311,
+ "versionNonce": 438650764,
+ "isDeleted": false,
+ "id": "zpYIOjWbVzmvnqh9DZn4u",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 621.9267415386879,
+ "y": 2200.003141102112,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 55,
+ "height": 26,
+ "seed": 848174279,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872552894,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "data:",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "data:"
+ },
+ {
+ "type": "rectangle",
+ "version": 448,
+ "versionNonce": 309908364,
+ "isDeleted": false,
+ "id": "QK8ETnxdqprXjm7W52Kdd",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 711.7681434957126,
+ "y": 2637.55751178056,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 173,
+ "height": 73.99999999999999,
+ "seed": 477784647,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 404,
+ "versionNonce": 215546676,
+ "isDeleted": false,
+ "id": "8RhamJiC35B4-ou8m42LO",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 751.7681434957126,
+ "y": 2659.05751178056,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 98,
+ "height": 26,
+ "seed": 1315799465,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "fallback(1)",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "fallback(1)"
+ },
+ {
+ "type": "rectangle",
+ "version": 540,
+ "versionNonce": 95322636,
+ "isDeleted": false,
+ "id": "SS5f0BrVXehYGOEa5X9ne",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 710.2681434957126,
+ "y": 2555.55751178056,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "#fab005",
+ "width": 173,
+ "height": 56,
+ "seed": 979601767,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "BVE9CJhFraT5fxaX009gL",
+ "type": "text"
+ },
+ {
+ "id": "BVE9CJhFraT5fxaX009gL",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "BVE9CJhFraT5fxaX009gL"
+ }
+ ],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 312,
+ "versionNonce": 1408988340,
+ "isDeleted": false,
+ "id": "BVE9CJhFraT5fxaX009gL",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 715.2681434957126,
+ "y": 2571.55751178056,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 914449545,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isValidating",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "SS5f0BrVXehYGOEa5X9ne",
+ "originalText": "isValidating"
+ },
+ {
+ "type": "rectangle",
+ "version": 592,
+ "versionNonce": 321544332,
+ "isDeleted": false,
+ "id": "9GEDngDGuqfDGTG9TlOR3",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 710.2681434957126,
+ "y": 2483.55751178056,
+ "strokeColor": "#1864ab",
+ "backgroundColor": "#15aabf",
+ "width": 173,
+ "height": 42,
+ "seed": 2015085703,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "vYN7kfHARtO3JdYIaif7j",
+ "type": "text"
+ },
+ {
+ "id": "vYN7kfHARtO3JdYIaif7j",
+ "type": "text"
+ },
+ {
+ "id": "vYN7kfHARtO3JdYIaif7j",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "vYN7kfHARtO3JdYIaif7j"
+ }
+ ],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 371,
+ "versionNonce": 1402125876,
+ "isDeleted": false,
+ "id": "vYN7kfHARtO3JdYIaif7j",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 715.2681434957126,
+ "y": 2492.55751178056,
+ "strokeColor": "#1864ab",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 2077617001,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isLoading",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "9GEDngDGuqfDGTG9TlOR3",
+ "originalText": "isLoading"
+ },
+ {
+ "type": "arrow",
+ "version": 359,
+ "versionNonce": 1923510028,
+ "isDeleted": false,
+ "id": "Mo-H-Fulwz9E3JWsIPtD_",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 715.7681434957126,
+ "y": 2746.55751178056,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 164,
+ "height": 2,
+ "seed": 1163893671,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 164,
+ 2
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 292,
+ "versionNonce": 1149556660,
+ "isDeleted": false,
+ "id": "1qM8CjGcbdG131pqOfKK6",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 748.7681434957126,
+ "y": 2760.55751178056,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 77,
+ "height": 26,
+ "seed": 1991677513,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "fetching",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "fetching"
+ },
+ {
+ "type": "rectangle",
+ "version": 738,
+ "versionNonce": 1419554188,
+ "isDeleted": false,
+ "id": "i2Xp_D9DySTF1jwUWmze9",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 917.2681434957126,
+ "y": 2636.55751178056,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 173.00000000000006,
+ "height": 73.00000000000001,
+ "seed": 1717276359,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 530,
+ "versionNonce": 1391968564,
+ "isDeleted": false,
+ "id": "Yu0xvL1SRmFeiejho6Lg-",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 968.7681434957126,
+ "y": 2659.05751178056,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 73,
+ "height": 26,
+ "seed": 1778312489,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "value(1)",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "value(1)"
+ },
+ {
+ "type": "arrow",
+ "version": 597,
+ "versionNonce": 1246174220,
+ "isDeleted": false,
+ "id": "XTF7n7FVWSLqOymEzCJlk",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1141.7681434957126,
+ "y": 2734.55751178056,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 168,
+ "height": 2,
+ "seed": 1705778663,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 168,
+ 2
+ ]
+ ]
+ },
+ {
+ "type": "rectangle",
+ "version": 743,
+ "versionNonce": 911309492,
+ "isDeleted": false,
+ "id": "6n-KLKuH69b579OrlP2d0",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1424.2681434957128,
+ "y": 2549.55751178056,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "#fab005",
+ "width": 173,
+ "height": 55,
+ "seed": 823810057,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "OBLx8Bl1i9R0n4ZM64DJ-",
+ "type": "text"
+ },
+ {
+ "id": "OBLx8Bl1i9R0n4ZM64DJ-",
+ "type": "text"
+ },
+ {
+ "id": "OBLx8Bl1i9R0n4ZM64DJ-",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "OBLx8Bl1i9R0n4ZM64DJ-"
+ }
+ ],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 514,
+ "versionNonce": 765442700,
+ "isDeleted": false,
+ "id": "OBLx8Bl1i9R0n4ZM64DJ-",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1429.2681434957128,
+ "y": 2565.05751178056,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 1180551431,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isValidating",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "6n-KLKuH69b579OrlP2d0",
+ "originalText": "isValidating"
+ },
+ {
+ "type": "rectangle",
+ "version": 548,
+ "versionNonce": 1228407860,
+ "isDeleted": false,
+ "id": "vXazvgvKaIyfv2xbzeF7y",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1139.2681434957126,
+ "y": 2637.55751178056,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 172.00000000000006,
+ "height": 71,
+ "seed": 772042473,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 310,
+ "versionNonce": 1490988940,
+ "isDeleted": false,
+ "id": "lKGn4gvlLBYgqHGQhrh1o",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 626.3816606654158,
+ "y": 2394.4268789739904,
+ "strokeColor": "#000000",
+ "backgroundColor": "#15aabf",
+ "width": 221,
+ "height": 26,
+ "seed": 1978470439,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872559073,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Key Change + Fallback",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Key Change + Fallback"
+ },
+ {
+ "type": "line",
+ "version": 219,
+ "versionNonce": 563174836,
+ "isDeleted": false,
+ "id": "gcvBTbOx4-8lyfF1JWsg8",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1097.2681434957126,
+ "y": 2477.55751178056,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "#15aabf",
+ "width": 3,
+ "height": 289,
+ "seed": 2001250761,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": null,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 3,
+ 289
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 448,
+ "versionNonce": 17008524,
+ "isDeleted": false,
+ "id": "lR7avuBHvY7TZP6nE-8y0",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1044.7681434957126,
+ "y": 2436.55751178056,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "#15aabf",
+ "width": 110,
+ "height": 26,
+ "seed": 2118546247,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Key Change",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Key Change"
+ },
+ {
+ "type": "text",
+ "version": 605,
+ "versionNonce": 679708468,
+ "isDeleted": false,
+ "id": "gUByQPmreSTplzK4zEjMb",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1180.7681434957126,
+ "y": 2659.05751178056,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 107,
+ "height": 26,
+ "seed": 1174571177,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "fallback(2)",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "fallback(2)"
+ },
+ {
+ "type": "rectangle",
+ "version": 585,
+ "versionNonce": 952284684,
+ "isDeleted": false,
+ "id": "WguJwYWvB0Pork0K46YLa",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1138.7681434957126,
+ "y": 2548.55751178056,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "#fab005",
+ "width": 173,
+ "height": 56,
+ "seed": 2009494119,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "OwilWRwNmEHxvUlIoSf8k",
+ "type": "text"
+ },
+ {
+ "id": "OwilWRwNmEHxvUlIoSf8k",
+ "type": "text"
+ },
+ {
+ "id": "OwilWRwNmEHxvUlIoSf8k",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "OwilWRwNmEHxvUlIoSf8k"
+ }
+ ],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 356,
+ "versionNonce": 846495924,
+ "isDeleted": false,
+ "id": "OwilWRwNmEHxvUlIoSf8k",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1143.7681434957126,
+ "y": 2564.55751178056,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 601721737,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isValidating",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "WguJwYWvB0Pork0K46YLa",
+ "originalText": "isValidating"
+ },
+ {
+ "type": "rectangle",
+ "version": 652,
+ "versionNonce": 1033376908,
+ "isDeleted": false,
+ "id": "QdivBt-TGkRRlJEwCyrAv",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1135.7681434957126,
+ "y": 2476.55751178056,
+ "strokeColor": "#1864ab",
+ "backgroundColor": "#15aabf",
+ "width": 173,
+ "height": 42,
+ "seed": 993627527,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "3UUfdlua29WkF_1gKm_16",
+ "type": "text"
+ },
+ {
+ "id": "3UUfdlua29WkF_1gKm_16",
+ "type": "text"
+ },
+ {
+ "id": "3UUfdlua29WkF_1gKm_16",
+ "type": "text"
+ },
+ {
+ "id": "3UUfdlua29WkF_1gKm_16",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "3UUfdlua29WkF_1gKm_16"
+ }
+ ],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 430,
+ "versionNonce": 386550324,
+ "isDeleted": false,
+ "id": "3UUfdlua29WkF_1gKm_16",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1140.7681434957126,
+ "y": 2485.55751178056,
+ "strokeColor": "#1864ab",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 1494561385,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isLoading",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "QdivBt-TGkRRlJEwCyrAv",
+ "originalText": "isLoading"
+ },
+ {
+ "type": "text",
+ "version": 374,
+ "versionNonce": 1832755980,
+ "isDeleted": false,
+ "id": "jaN2RPWTkxJq4UKk6nW8x",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1190.7681434957126,
+ "y": 2754.55751178056,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 77,
+ "height": 26,
+ "seed": 1406944423,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "fetching",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "fetching"
+ },
+ {
+ "type": "rectangle",
+ "version": 676,
+ "versionNonce": 1227547572,
+ "isDeleted": false,
+ "id": "meBDVJ3ooCK77BFOeTXc6",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1351.2681434957128,
+ "y": 2633.05751178056,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 254,
+ "height": 71,
+ "seed": 443874633,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 678,
+ "versionNonce": 1580032396,
+ "isDeleted": false,
+ "id": "Qb8rusB9PvrZcmGExWn-2",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1441.2681434957128,
+ "y": 2657.05751178056,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 82,
+ "height": 26,
+ "seed": 828053447,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "value(2)",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "value(2)"
+ },
+ {
+ "type": "line",
+ "version": 548,
+ "versionNonce": 1862028596,
+ "isDeleted": false,
+ "id": "DNJWtKpLHm78Qr9CRE6KK",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1416.2681434957128,
+ "y": 2470.55751178056,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "#15aabf",
+ "width": 0,
+ "height": 147,
+ "seed": 2002283561,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": null,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 147
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 562,
+ "versionNonce": 1201754124,
+ "isDeleted": false,
+ "id": "aM1FK248ewZaF45gQU9GD",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1355.2681434957128,
+ "y": 2433.55751178056,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "#15aabf",
+ "width": 106,
+ "height": 26,
+ "seed": 433227495,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Revalidate",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Revalidate"
+ },
+ {
+ "type": "rectangle",
+ "version": 596,
+ "versionNonce": 2045019828,
+ "isDeleted": false,
+ "id": "GiUibUatWu7WRRcxtI1XA",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1629.2681434957128,
+ "y": 2632.05751178056,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 172.00000000000006,
+ "height": 71,
+ "seed": 128646921,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 677,
+ "versionNonce": 1771269772,
+ "isDeleted": false,
+ "id": "V7oZ1QERdHVh3cS91WNUl",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1660.2681434957128,
+ "y": 2654.05751178056,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 124,
+ "height": 26,
+ "seed": 1540297223,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "new value(2)",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "new value(2)"
+ },
+ {
+ "type": "text",
+ "version": 381,
+ "versionNonce": 1590518668,
+ "isDeleted": false,
+ "id": "9a_kKwKgIzm2Z0IjEh_Z4",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 637.0224708709327,
+ "y": 2667.55751178056,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 55,
+ "height": 26,
+ "seed": 1508390377,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872563409,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "data:",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "data:"
+ },
+ {
+ "type": "rectangle",
+ "version": 482,
+ "versionNonce": 1958591756,
+ "isDeleted": false,
+ "id": "-obYwrHQGLpaItpqHexYm",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 712.8987763022823,
+ "y": 3089.8106344083985,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 173,
+ "height": 73.99999999999999,
+ "seed": 1475085001,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 437,
+ "versionNonce": 215050676,
+ "isDeleted": false,
+ "id": "LeS1s-a6H698PwNaqcMqV",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 752.8987763022823,
+ "y": 3111.3106344083985,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 98,
+ "height": 26,
+ "seed": 1860250183,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "fallback(1)",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "fallback(1)"
+ },
+ {
+ "type": "rectangle",
+ "version": 575,
+ "versionNonce": 1922710412,
+ "isDeleted": false,
+ "id": "6j9Rz1BMk4ED8YdCTmiaX",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 711.3987763022823,
+ "y": 3007.8106344083985,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "#fab005",
+ "width": 173,
+ "height": 56,
+ "seed": 1255326121,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "NGoZB15IE4xoOlF-S-nY5",
+ "type": "text"
+ },
+ {
+ "id": "NGoZB15IE4xoOlF-S-nY5",
+ "type": "text"
+ },
+ {
+ "id": "NGoZB15IE4xoOlF-S-nY5",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "NGoZB15IE4xoOlF-S-nY5"
+ }
+ ],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 346,
+ "versionNonce": 1205998388,
+ "isDeleted": false,
+ "id": "NGoZB15IE4xoOlF-S-nY5",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 716.3987763022823,
+ "y": 3023.8106344083985,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 301091175,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408320,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isValidating",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "6j9Rz1BMk4ED8YdCTmiaX",
+ "originalText": "isValidating"
+ },
+ {
+ "type": "rectangle",
+ "version": 627,
+ "versionNonce": 761457164,
+ "isDeleted": false,
+ "id": "rwYXrQpHbnAHN197NhMMb",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 711.3987763022823,
+ "y": 2935.8106344083985,
+ "strokeColor": "#1864ab",
+ "backgroundColor": "#15aabf",
+ "width": 173,
+ "height": 42,
+ "seed": 1950262409,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "rAjdPbbaQQfSV10qiYBOT",
+ "type": "text"
+ },
+ {
+ "id": "rAjdPbbaQQfSV10qiYBOT",
+ "type": "text"
+ },
+ {
+ "id": "rAjdPbbaQQfSV10qiYBOT",
+ "type": "text"
+ },
+ {
+ "id": "rAjdPbbaQQfSV10qiYBOT",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "rAjdPbbaQQfSV10qiYBOT"
+ }
+ ],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 405,
+ "versionNonce": 2116127924,
+ "isDeleted": false,
+ "id": "rAjdPbbaQQfSV10qiYBOT",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 716.3987763022823,
+ "y": 2944.8106344083985,
+ "strokeColor": "#1864ab",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 1876067463,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isLoading",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "rwYXrQpHbnAHN197NhMMb",
+ "originalText": "isLoading"
+ },
+ {
+ "type": "arrow",
+ "version": 392,
+ "versionNonce": 403818636,
+ "isDeleted": false,
+ "id": "XqPo2HNpHJM9q4PkTwP7P",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 716.8987763022823,
+ "y": 3198.8106344083985,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 164,
+ "height": 2,
+ "seed": 267070313,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 164,
+ 2
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 325,
+ "versionNonce": 20305460,
+ "isDeleted": false,
+ "id": "_iHlnrQt-vZVk8HNbdziN",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 749.8987763022823,
+ "y": 3212.8106344083985,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 77,
+ "height": 26,
+ "seed": 406739879,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "fetching",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "fetching"
+ },
+ {
+ "type": "rectangle",
+ "version": 771,
+ "versionNonce": 912316172,
+ "isDeleted": false,
+ "id": "UVAEdnKioqetobHZXEz2X",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 918.3987763022823,
+ "y": 3088.8106344083985,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 173.00000000000006,
+ "height": 73.00000000000001,
+ "seed": 1445189193,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 563,
+ "versionNonce": 2114252724,
+ "isDeleted": false,
+ "id": "Nj8h4rs1lhG6GLtfIoLEi",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 969.8987763022823,
+ "y": 3111.3106344083985,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 73,
+ "height": 26,
+ "seed": 1868713671,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "value(1)",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "value(1)"
+ },
+ {
+ "type": "arrow",
+ "version": 630,
+ "versionNonce": 80352652,
+ "isDeleted": false,
+ "id": "9PNnPgq1RY62-qWmbGaTY",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1142.8987763022822,
+ "y": 3186.8106344083985,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 168,
+ "height": 2,
+ "seed": 1774462249,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": "arrow",
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 168,
+ 2
+ ]
+ ]
+ },
+ {
+ "type": "rectangle",
+ "version": 778,
+ "versionNonce": 347396404,
+ "isDeleted": false,
+ "id": "pllGeq__Vv3Sy-zrAFoZJ",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1425.3987763022824,
+ "y": 3001.8106344083985,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "#fab005",
+ "width": 173,
+ "height": 55,
+ "seed": 534437351,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "FGFotr7EZlk-mCyeu0T0_",
+ "type": "text"
+ },
+ {
+ "id": "FGFotr7EZlk-mCyeu0T0_",
+ "type": "text"
+ },
+ {
+ "id": "FGFotr7EZlk-mCyeu0T0_",
+ "type": "text"
+ },
+ {
+ "id": "FGFotr7EZlk-mCyeu0T0_",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "FGFotr7EZlk-mCyeu0T0_"
+ }
+ ],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 548,
+ "versionNonce": 1619493900,
+ "isDeleted": false,
+ "id": "FGFotr7EZlk-mCyeu0T0_",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1430.3987763022824,
+ "y": 3017.3106344083985,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 104306697,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isValidating",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "pllGeq__Vv3Sy-zrAFoZJ",
+ "originalText": "isValidating"
+ },
+ {
+ "type": "rectangle",
+ "version": 581,
+ "versionNonce": 1890292404,
+ "isDeleted": false,
+ "id": "We9aiA07XYKROyzIczfPg",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1140.3987763022822,
+ "y": 3089.8106344083985,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 172.00000000000006,
+ "height": 71,
+ "seed": 1286899975,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 379,
+ "versionNonce": 446139444,
+ "isDeleted": false,
+ "id": "tyqI5KzygcqyZvnrn-5rL",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 632.296248081498,
+ "y": 2851.281007174106,
+ "strokeColor": "#000000",
+ "backgroundColor": "#15aabf",
+ "width": 398,
+ "height": 26,
+ "seed": 1836691177,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872568882,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Key Change + Previous Data + Fallback",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Key Change + Previous Data + Fallback"
+ },
+ {
+ "type": "line",
+ "version": 252,
+ "versionNonce": 1619991604,
+ "isDeleted": false,
+ "id": "9ryLZmvw2TQRg0-LfGpc5",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1098.3987763022824,
+ "y": 2929.8106344083985,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "#15aabf",
+ "width": 3,
+ "height": 289,
+ "seed": 57397287,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": null,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 3,
+ 289
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 481,
+ "versionNonce": 145158412,
+ "isDeleted": false,
+ "id": "pgAj5KMbnf6uYvqC0LnZD",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1045.8987763022822,
+ "y": 2888.8106344083985,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "#15aabf",
+ "width": 110,
+ "height": 26,
+ "seed": 370534857,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Key Change",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Key Change"
+ },
+ {
+ "type": "text",
+ "version": 635,
+ "versionNonce": 1850045876,
+ "isDeleted": false,
+ "id": "hlJVsC9l19LF85JDKspWQ",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1181.8987763022824,
+ "y": 3111.3106344083985,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 73,
+ "height": 26,
+ "seed": 1844261703,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "value(1)",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "value(1)"
+ },
+ {
+ "type": "rectangle",
+ "version": 620,
+ "versionNonce": 456978316,
+ "isDeleted": false,
+ "id": "Boon9aMQQkYRoLFNH-TO0",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1139.8987763022822,
+ "y": 3000.8106344083985,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "#fab005",
+ "width": 173,
+ "height": 56,
+ "seed": 422037673,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "GdYbNrg2YuX0CfmULECgW",
+ "type": "text"
+ },
+ {
+ "id": "GdYbNrg2YuX0CfmULECgW",
+ "type": "text"
+ },
+ {
+ "id": "GdYbNrg2YuX0CfmULECgW",
+ "type": "text"
+ },
+ {
+ "id": "GdYbNrg2YuX0CfmULECgW",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "GdYbNrg2YuX0CfmULECgW"
+ }
+ ],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 390,
+ "versionNonce": 1995743028,
+ "isDeleted": false,
+ "id": "GdYbNrg2YuX0CfmULECgW",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1144.8987763022822,
+ "y": 3016.8106344083985,
+ "strokeColor": "#c92a2a",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 1150628455,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isValidating",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "Boon9aMQQkYRoLFNH-TO0",
+ "originalText": "isValidating"
+ },
+ {
+ "type": "rectangle",
+ "version": 687,
+ "versionNonce": 395255308,
+ "isDeleted": false,
+ "id": "7yiHj7_DavbMiO75thqAu",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1136.8987763022822,
+ "y": 2928.8106344083985,
+ "strokeColor": "#1864ab",
+ "backgroundColor": "#15aabf",
+ "width": 173,
+ "height": 42,
+ "seed": 1616563081,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [
+ {
+ "id": "6_XBHa2TIyUiES2nPvQ_y",
+ "type": "text"
+ },
+ {
+ "id": "6_XBHa2TIyUiES2nPvQ_y",
+ "type": "text"
+ },
+ {
+ "id": "6_XBHa2TIyUiES2nPvQ_y",
+ "type": "text"
+ },
+ {
+ "id": "6_XBHa2TIyUiES2nPvQ_y",
+ "type": "text"
+ },
+ {
+ "id": "6_XBHa2TIyUiES2nPvQ_y",
+ "type": "text"
+ },
+ {
+ "type": "text",
+ "id": "6_XBHa2TIyUiES2nPvQ_y"
+ }
+ ],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 464,
+ "versionNonce": 1124243636,
+ "isDeleted": false,
+ "id": "6_XBHa2TIyUiES2nPvQ_y",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1141.8987763022822,
+ "y": 2937.8106344083985,
+ "strokeColor": "#1864ab",
+ "backgroundColor": "transparent",
+ "width": 163,
+ "height": 24,
+ "seed": 3165575,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "isLoading",
+ "baseline": 17,
+ "textAlign": "center",
+ "verticalAlign": "middle",
+ "containerId": "7yiHj7_DavbMiO75thqAu",
+ "originalText": "isLoading"
+ },
+ {
+ "type": "text",
+ "version": 407,
+ "versionNonce": 1452768396,
+ "isDeleted": false,
+ "id": "Y20W-l3e_saX6dt-GFuJW",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1191.8987763022824,
+ "y": 3206.8106344083985,
+ "strokeColor": "#862e9c",
+ "backgroundColor": "#15aabf",
+ "width": 77,
+ "height": 26,
+ "seed": 334131817,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "fetching",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "fetching"
+ },
+ {
+ "type": "rectangle",
+ "version": 709,
+ "versionNonce": 504717876,
+ "isDeleted": false,
+ "id": "JN164kTgKkkjOmmjlo-HU",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1352.3987763022824,
+ "y": 3085.3106344083985,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 254,
+ "height": 71,
+ "seed": 1274741927,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 711,
+ "versionNonce": 34605836,
+ "isDeleted": false,
+ "id": "sZLyb8U-QFWZNGpaBtKGd",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1442.3987763022824,
+ "y": 3109.3106344083985,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 82,
+ "height": 26,
+ "seed": 1218592073,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "value(2)",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "value(2)"
+ },
+ {
+ "type": "line",
+ "version": 581,
+ "versionNonce": 1863515060,
+ "isDeleted": false,
+ "id": "51DRbuFYdETkxNpiyNFKn",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1417.3987763022824,
+ "y": 2922.8106344083985,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "#15aabf",
+ "width": 0,
+ "height": 147,
+ "seed": 933353415,
+ "groupIds": [],
+ "strokeSharpness": "round",
+ "boundElements": [],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false,
+ "startBinding": null,
+ "endBinding": null,
+ "lastCommittedPoint": null,
+ "startArrowhead": null,
+ "endArrowhead": null,
+ "points": [
+ [
+ 0,
+ 0
+ ],
+ [
+ 0,
+ 147
+ ]
+ ]
+ },
+ {
+ "type": "text",
+ "version": 595,
+ "versionNonce": 2087897484,
+ "isDeleted": false,
+ "id": "ohg_PD-xV0p3gI3bFSgMK",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1356.3987763022824,
+ "y": 2885.8106344083985,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "#15aabf",
+ "width": 106,
+ "height": 26,
+ "seed": 1843244073,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "Revalidate",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "Revalidate"
+ },
+ {
+ "type": "rectangle",
+ "version": 629,
+ "versionNonce": 1344365876,
+ "isDeleted": false,
+ "id": "R7bZyYHto39eS0ou8AQOR",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1630.3987763022824,
+ "y": 3084.3106344083985,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 172.00000000000006,
+ "height": 71,
+ "seed": 692143847,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "text",
+ "version": 710,
+ "versionNonce": 1224325132,
+ "isDeleted": false,
+ "id": "oG5YEm7Mlu66zElwDyzL4",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 1661.3987763022824,
+ "y": 3106.3106344083985,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 124,
+ "height": 26,
+ "seed": 2072526601,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872408321,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "new value(2)",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "new value(2)"
+ },
+ {
+ "type": "text",
+ "version": 405,
+ "versionNonce": 1649937548,
+ "isDeleted": false,
+ "id": "DdTxZWybeUxdYj3VG6KiQ",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "solid",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 634.6827309117947,
+ "y": 3119.8106344083985,
+ "strokeColor": "#000000",
+ "backgroundColor": "transparent",
+ "width": 55,
+ "height": 26,
+ "seed": 258331143,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872572095,
+ "link": null,
+ "locked": false,
+ "fontSize": 20,
+ "fontFamily": 1,
+ "text": "data:",
+ "baseline": 18,
+ "textAlign": "left",
+ "verticalAlign": "top",
+ "containerId": null,
+ "originalText": "data:"
+ },
+ {
+ "type": "rectangle",
+ "version": 367,
+ "versionNonce": 4402868,
+ "isDeleted": false,
+ "id": "9SPdPUm0w_2mgmCR-h7Tz",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 614.7174396105432,
+ "y": 982.1670280320277,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "transparent",
+ "width": 1210.9077358360364,
+ "height": 418.33413843075,
+ "seed": 1981925812,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872474798,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "rectangle",
+ "version": 420,
+ "versionNonce": 554416308,
+ "isDeleted": false,
+ "id": "cBsL94s5t7EYd-kbPTSn7",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 607.041255728865,
+ "y": 1450.7605045639884,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "transparent",
+ "width": 1210.9077358360364,
+ "height": 418.33413843075,
+ "seed": 91199540,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872506656,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "rectangle",
+ "version": 450,
+ "versionNonce": 541671732,
+ "isDeleted": false,
+ "id": "AiiDTwKfUUq1xH3shu2YD",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 605.8844648069626,
+ "y": 1911.1632914811794,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "transparent",
+ "width": 1210.9077358360364,
+ "height": 418.33413843075,
+ "seed": 502956596,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872516268,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "rectangle",
+ "version": 444,
+ "versionNonce": 1441266996,
+ "isDeleted": false,
+ "id": "d3PM5Quw5oezQfpn8rmYf",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 610.5116284945725,
+ "y": 2373.8796602421753,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "transparent",
+ "width": 1210.9077358360364,
+ "height": 418.33413843075,
+ "seed": 1567816372,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872524462,
+ "link": null,
+ "locked": false
+ },
+ {
+ "type": "rectangle",
+ "version": 439,
+ "versionNonce": 25553036,
+ "isDeleted": false,
+ "id": "DWQ6aIClOm1-zU3bw7aiS",
+ "fillStyle": "hachure",
+ "strokeWidth": 1,
+ "strokeStyle": "dotted",
+ "roughness": 1,
+ "opacity": 100,
+ "angle": 0,
+ "x": 609.3548375726702,
+ "y": 2837.7528199250733,
+ "strokeColor": "#d9480f",
+ "backgroundColor": "transparent",
+ "width": 1210.9077358360364,
+ "height": 418.33413843075,
+ "seed": 1742354100,
+ "groupIds": [],
+ "strokeSharpness": "sharp",
+ "boundElements": [],
+ "updated": 1661872529798,
+ "link": null,
+ "locked": false
+ }
+ ],
+ "appState": {
+ "gridSize": null,
+ "viewBackgroundColor": "#ffffff"
+ },
+ "files": {}
+}
\ No newline at end of file
diff --git a/components/video.js b/components/video.js
index e4ce6c18..7c25e47b 100644
--- a/components/video.js
+++ b/components/video.js
@@ -1,52 +1,65 @@
-import { useRef, useCallback, useEffect } from 'react'
-import { useInView } from 'react-intersection-observer'
-import 'intersection-observer'
+import { useRef, useCallback, useEffect } from "react";
+import { useInView } from "react-intersection-observer";
+import "intersection-observer";
export default ({ src, caption, ratio }) => {
const [inViewRef, inView] = useInView({
threshold: 1,
- })
- const videoRef = useRef()
+ });
+ const videoRef = useRef();
const setRefs = useCallback(
(node) => {
// Ref's from useRef needs to have the node assigned to `current`
- videoRef.current = node
+ videoRef.current = node;
// Callback refs, like the one from `useInView`, is a function that takes the node as an argument
- inViewRef(node)
+ inViewRef(node);
if (node) {
- node.addEventListener('click', function () {
+ node.addEventListener("click", function () {
if (this.paused) {
- this.play()
+ this.play();
} else {
- this.pause()
+ this.pause();
}
- })
+ });
}
},
[inViewRef]
- )
+ );
useEffect(() => {
if (!videoRef || !videoRef.current) {
- return
+ return;
}
if (inView) {
- videoRef.current.play()
+ videoRef.current.play();
} else {
- videoRef.current.pause()
+ videoRef.current.pause();
}
- }, [inView])
+ }, [inView]);
return (
-
-
-
+
+
+
- {caption &&
{caption} }
+ {caption && (
+
+ {caption}
+
+ )}
- )
-}
+ );
+};
diff --git a/next.config.js b/next.config.js
index 0eb3d08a..2edfd15b 100644
--- a/next.config.js
+++ b/next.config.js
@@ -32,6 +32,11 @@ module.exports = withNextra({
destination: "/docs/advanced/cache",
statusCode: 301,
},
+ {
+ source: "/docs/options",
+ destination: "/docs/api",
+ statusCode: 301
+ },
{
source: "/change-log",
destination: "/docs/change-log",
diff --git a/pages/blog/meta.en-US.json b/pages/blog/meta.en-US.json
index e86c2ad6..1f25fff2 100644
--- a/pages/blog/meta.en-US.json
+++ b/pages/blog/meta.en-US.json
@@ -1,3 +1,4 @@
{
+ "swr-v2": "Announcing SWR 2.0",
"swr-v1": "Announcing SWR 1.0"
}
diff --git a/pages/blog/meta.es-ES.json b/pages/blog/meta.es-ES.json
index e86c2ad6..1f25fff2 100644
--- a/pages/blog/meta.es-ES.json
+++ b/pages/blog/meta.es-ES.json
@@ -1,3 +1,4 @@
{
+ "swr-v2": "Announcing SWR 2.0",
"swr-v1": "Announcing SWR 1.0"
}
diff --git a/pages/blog/meta.ja.json b/pages/blog/meta.ja.json
index 2131477f..a87ff7b9 100644
--- a/pages/blog/meta.ja.json
+++ b/pages/blog/meta.ja.json
@@ -1,3 +1,4 @@
{
+ "swr-v2": "SWR 2.0 の発表",
"swr-v1": "SWR 1.0 の発表"
}
diff --git a/pages/blog/meta.ko.json b/pages/blog/meta.ko.json
index 1e284f37..3db2fd0e 100644
--- a/pages/blog/meta.ko.json
+++ b/pages/blog/meta.ko.json
@@ -1,3 +1,4 @@
{
+ "swr-v2": "Announcing SWR 2.0",
"swr-v1": "SWR 1.0 알림"
}
diff --git a/pages/blog/meta.pt-BR.json b/pages/blog/meta.pt-BR.json
index 0ca68d6b..e8f96eb2 100644
--- a/pages/blog/meta.pt-BR.json
+++ b/pages/blog/meta.pt-BR.json
@@ -1,3 +1,4 @@
-{
- "swr-v1": "Anunciando SWR 1.0"
-}
+{
+ "swr-v2": "Announcing SWR 2.0",
+ "swr-v1": "Anunciando SWR 1.0"
+}
diff --git a/pages/blog/meta.ru.json b/pages/blog/meta.ru.json
index af9b141a..5dfaa1f1 100644
--- a/pages/blog/meta.ru.json
+++ b/pages/blog/meta.ru.json
@@ -1,3 +1,4 @@
{
+ "swr-v2": "Announcing SWR 2.0",
"swr-v1": "Представляем SWR 1.0"
}
diff --git a/pages/blog/meta.zh-CN.json b/pages/blog/meta.zh-CN.json
index ba68941a..71fd58bd 100644
--- a/pages/blog/meta.zh-CN.json
+++ b/pages/blog/meta.zh-CN.json
@@ -1,3 +1,4 @@
{
+ "swr-v2": "Announcing SWR 2.0",
"swr-v1": "SWR 1.0 发布"
}
diff --git a/pages/blog/swr-v2.en-US.mdx b/pages/blog/swr-v2.en-US.mdx
new file mode 100644
index 00000000..8d53655d
--- /dev/null
+++ b/pages/blog/swr-v2.en-US.mdx
@@ -0,0 +1,410 @@
+---
+image: https://assets.vercel.com/image/upload/v1670542323/swr/v2.png
+description: 'Announcing SWR 2.0: new mutation APIs and improvd optimistic UI capabilities, new DevTools, better support for concurrent rendering, and more.'
+date: December 9th, 2022
+---
+
+import Callout from 'nextra-theme-docs/callout'
+import Bleed from 'nextra-theme-docs/bleed'
+
+import Authors, { Author } from 'components/authors'
+import Video from 'components/video'
+
+# Announcing SWR 2.0
+
+
+
+
+
+
+
+
+Today, we are excited to announce the release of SWR 2.0! This new version comes with a lot of improvements and new features: new mutation APIs and improvements to optimistic UI scenarios, new DevTools, better support for concurrent rendering, etc. We would like to give a big shout-out to all the contributors and maintainers who helped make this release possible.
+
+## Mutation and Optimistic UI
+
+### useSWRMutation
+
+Mutation is an important part of the data-fetching process. They allow you to make changes to your data both locally and remotely. Our existing `mutate` API allows you to revalidate and mutate resources manually. In SWR 2.0, the new hook `useSWRMutation` makes it even simpler to remotely change data using a declarative API. You can set up a mutation using the hook, and then activate it later:
+
+```jsx {11,16}
+import useSWRMutation from 'swr/mutation'
+
+async function sendRequest(url, { arg }) {
+ return fetch(url, {
+ method: 'POST',
+ body: JSON.stringify(arg)
+ })
+}
+
+function App() {
+ const { trigger, isMutating } = useSWRMutation('/api/user', sendRequest)
+
+ return (
+ trigger({ username: 'johndoe' })}
+ >{
+ isMutating ? 'Creating...' : 'Create User'
+ }
+ )
+}
+```
+
+The example above defines a `sendRequest` mutation that affects the `'/api/user'` resource. Unlike `useSWR`, `useSWRMutation` will not immediately start the request upon rendering. Instead, it returns a `trigger` function that can later be called to manually start the mutation.
+
+The `sendRequest` function will be called when the button is clicked, with the extra argument `{ username: 'johndoe' }`. The value of `isMutating` will be set to `true` until the mutation has finished.
+
+Additionally, this new hook addresses other issues you may have with mutations:
+
+- Optimistically update the UI while data is being mutated
+- Automatically revert when mutation fails
+- Avoid any potential race conditions between `useSWR` and other mutations of the same resource
+- Populate the `useSWR` cache after mutation completes
+- ...
+
+You can find in-depth API references and examples by reading the [docs](/docs/mutation#useswrmutation) or scrolling through the next few sections.
+
+### Optimistic UI
+
+Optimistic UI is an excellent model for creating websites that feel fast and responsive; however, it can be difficult to implement correctly. SWR 2.0 has added some new powerful options to make it easier.
+
+Let’s say we have an API that adds a new todo to the todo list and sends it to the server:
+
+```jsx
+await addNewTodo('New Item')
+```
+
+In our UI, we use a `useSWR` hook to display the todo list, with an “Add New Item” button that triggers this request and asks SWR to re-fetch the data via `mutate()`:
+
+```jsx {7,8}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ await addNewTodo('New Item')
+ mutate()
+ }}>
+ Add New Item
+
+>
+```
+
+However, the `await addNewTodo(...)` request could be very slow. When it’s ongoing, users still see the old list even if we can already know what the new list will look like. With the new `optimisticData` option, we can show the new list optimistically, before the server responds:
+
+```jsx {8}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+SWR will immediately update the `data` with the `optimisticData` value, and then send the request to the server. Once the request finishes, SWR will revalidate the resource to ensure it’s the latest.
+
+Like many APIs, if the `addNewTodo(...)` request returns us the latest data from the server, we can directly show that result, too (instead of starting a new revalidation)! There’s the new `populateCache` option to tell SWR to update the local data with the mutate response:
+
+```jsx {9}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ populateCache: true,
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+At the same time, we don’t need another revalidation afterward as the response data is from the source of truth, we can disable it with the `revalidate` option:
+
+```jsx {10}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ populateCache: true,
+ revalidate: false,
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+Lastly, if `addNewTodo(...)` fails with an exception, we can revert the optimistic data (`[...data, 'New Item']`) we just set, by setting `rollbackOnError` to `true` (which is also the default option). When that happens, SWR will roll back `data` to the previous value.
+
+```jsx {11}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ populateCache: true,
+ revalidate: false,
+ rollbackOnError: true,
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+All these APIs are supported in the new `useSWRMutation` hook as well. To learn more about them, you can check out our [docs](/docs/mutation#optimistic-updates). And here is a demo showing that behavior:
+
+
+
+### Mutate Multiple Keys
+
+The global `mutate` API now accepts a filter function, where you can mutate or revalidate specific keys. This will be helpful for use cases such as invalidating all the cached data. To learn more, you can read [Mutate Multiple Keys](/docs/mutation#mutate-multiple-items) in the docs.
+
+```jsx
+import { mutate } from 'swr'
+// Or from the hook if you have customized your cache provider:
+// { mutate } = useSWRConfig()
+
+// Mutate single resource
+mutate(key)
+
+// Mutate multiple resources and clear the cache (set to undefined)
+mutate(
+ key => typeof key === 'string' && key.startsWith('/api/item?id='),
+ undefined,
+ { revalidate: false }
+)
+```
+
+## SWR DevTools
+
+[SWRDevTools](https://swr-devtools.vercel.app) is a browser extension that helps you debug your SWR cache and the fetch results. Check our [devtools](/docs/advanced/devtools) section for how to use devtools in your application.
+
+![](/img/devtools/cache-view.jpg)
+
+## Preloading Data
+
+Preloading data can improve the user experience tremendously. If you know the resource is going to be used later in the application, you can use the new `preload` API to start fetching it early:
+
+```jsx {6}
+import useSWR, { preload } from 'swr'
+
+const fetcher = (url) => fetch(url).then((res) => res.json())
+
+// You can call the preload function in anywhere
+preload('/api/user', fetcher)
+
+function Profile() {
+ // The component that actually uses the data:
+ const { data, error } = useSWR('/api/user', fetcher)
+ // ...
+}
+
+export function Page () {
+ return
+}
+```
+
+In this example, the `preload` API is called in the global scope. This means that we start to preload the resource before React even starts to render anything.
+And when the `Profile` component is being rendered, the data can probably be available already. If it’s still ongoing, the `useSWR` hook will reuse that ongoing preloading request instead of starting a new one.
+
+The `preload` API can also be used in cases like preloading data for another page that will likely be rendered. More information about prefetching data with SWR can be found [here](/docs/prefetching).
+
+## `isLoading`
+
+`isLoading` is a new state returned by `useSWR`, that indicates **if the request is still ongoing, and there is no data loaded yet**. Previously, the `isValidating` state represents both the initial loading state and revalidating state so we had to check if both `data` and `error` are `undefined` to determine if it was the initial loading state.
+
+Now, it is so easy that you can directly use the `isLoading` value to render a loading message:
+
+```jsx
+import useSWR from 'swr'
+
+function Profile() {
+ const { data, isLoading } = useSWR('/api/user', fetcher)
+
+ if (isLoading) return loading...
+ return hello {data.name}!
+}
+```
+
+Note that `isValidating` is still present so you can still use it to show a loading indicator for revalidations.
+
+
+ We have added the new [Understanding SWR](/docs/advanced/understanding) page to describe how SWR returns values, which includes the difference between `isValidating` and `isLoading`, and how to combine them to improve user experience.
+
+
+## Preserving Previous State
+
+The `keepPreviousData` option is a new addition that allows you to keep the data that was fetched before. This improves UX immensely when you’re fetching data based on user actions happening in real time, like with a live search feature, where the resource’s `key` keeps changing:
+
+```jsx {5}
+function Search() {
+ const [search, setSearch] = React.useState('');
+
+ const { data, isLoading } = useSWR(`/search?q=${search}`, fetcher, {
+ keepPreviousData: true
+ })
+
+ return (
+
+
setSearch(e.target.value)}
+ placeholder="Search..."
+ />
+
+
+ {data?.products.map(item =>
)
+
+
+ );
+}
+```
+
+
+
+Check the code on [CodeSandbox](https://codesandbox.io/s/swr-keeppreviousdata-fsjz3m) and you can read more about it [here](/docs/advanced/understanding#return-previous-data-for-better-ux).
+
+## Extending Configurations
+
+`SWRConfig` can now accept a function value. When you have multiple levels of ``, the inner receives the parent configuration and returns a new one. This change makes it more flexible to configure SWR in a large codebase. More information can be found [here](/docs/global-configuration).
+
+```jsx
+ ({
+ dedupingInterval: parentConfig.dedupingInterval * 5,
+ refreshInterval: 100,
+ })}
+>
+
+
+```
+
+## Improved React 18 Support
+
+SWR has updated its internal code to use `useSyncExternalStore` and `startTransition` APIs in React 18. These ensure stronger consistency when rendering UI concurrently. This change doesn’t require any user code changes and all developers will benefit from it directly. Shims are included for React 17 and below.
+
+SWR 2.0 and all the new features are still compatible with React 16 and 17.
+
+## Migration Guide
+
+### Fetcher No Longer Accepts Multiple Arguments
+
+`key` is now passed as a single argument.
+
+```diff
+- useSWR([1, 2, 3], (a, b, c) => {
++ useSWR([1, 2, 3], ([a, b, c]) => {
+ assert(a === 1)
+ assert(b === 2)
+ assert(c === 3)
+})
+```
+
+### Global Mutate No Longer Accepts a `getKey` Function
+
+Now, if you pass a function to the global `mutate`, it will be used as a [filter](/blog/swr-v2#mutate-multiple-keys). Previously, you can pass a function that returns a key to the global `mutate`:
+
+```diff
+- mutate(() => '/api/item') // a function to return a key
++ mutate('/api/item') // to mutate the key, directly pass it
+```
+
+### New Required Property `keys()` for Cache Interface
+
+When you use your own cache implementation, the Cache interface now requires a `keys()` method that returns all keys in the cache object, similar to the JavaScript Map instances.
+
+```diff
+interface Cache {
+ get(key: string): Data | undefined
+ set(key: string, value: Data): void
+ delete(key: string): void
++ keys(): IterableIterator
+}
+```
+
+### Changed Cache Internal Structure
+
+The internal structure of the cache data will be an object that holds all the current states.
+
+```diff
+- assert(cache.get(key) === data)
++ assert(cache.get(key) === { data, error, isValidating })
+
+// getter
+- cache.get(key)
++ cache.get(key)?.data
+
+// setter
+- cache.set(key, data)
++ cache.set(key, { ...cache.get(key), data })
+```
+
+
+ You should not write to the cache directly, it might cause undefined behavior.
+
+
+### `SWRConfig.default` Is Renamed as `SWRConfig.defaultValue`
+
+`SWRConfig.defaultValue` is the property for accessing the default SWR config.
+
+```diff
+- SWRConfig.default
++ SWRConfig.defaultValue
+```
+
+### Type `InfiniteFetcher` Is Renamed as `SWRInfiniteFetcher`
+
+```diff
+- import type { InfiniteFetcher } from 'swr/infinite'
++ import type { SWRInfiniteFetcher } from 'swr/infinite'
+```
+
+### Avoid Suspense on Server
+
+If you want to use `suspense: true` with SWR on the server-side, including pre-rendering in Next.js, then you must provide initial data via [`fallbackData` or `fallback`](/docs/with-nextjs#pre-rendering-with-default-data). Today, this means that you can't use Suspense to fetch data on the server side. Your other two options are doing fully client-side data-fetching or getting your framework to fetch the data for you (like getStaticProps does in Next.js).
+
+### ES2018 as the Build Target
+
+If you want to support IE 11, you have to target ES5 in your framework or a bundler. This change has made a performance improvement on SSR, and keeps the bundle size small.
+
+## Changelog
+
+Read the full Changelog [on GitHub](https://github.com/vercel/swr/releases).
+
+## The Future & Thank You!
+
+With the new release of [Next.js 13](https://nextjs.org/blog/next-13), we see a lot of exciting new things as well as paradigm shifts in the React ecosystem: [React Server Components](https://beta.nextjs.org/docs/rendering/server-and-client-components), streaming SSR, [async components](https://beta.nextjs.org/docs/data-fetching/fetching#asyncawait-in-server-components), and the [`use` hook](https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md#usepromise). Many of them are related to data-fetching, and some of them have overlapping use cases with SWR.
+
+However, the goal of the SWR project remains the same. We want it to be a drop-in library that is lightweight, framework agnostic, and a little bit _opinionated_ (i.e. revalidate upon focus). Instead of trying to be a standard solution, we want to focus on innovations that make the UX better. In the meantime, we are also doing research on how to improve SWR with these new abilities of React.
+
+We want to thank every one of the [143](https://github.com/vercel/swr/graphs/contributors) contributors (+ [106](https://github.com/vercel/swr-site/graphs/contributors) docs contributors), as well as those who helps us out or gave feedback. A special thanks goes to [Toru Kobayashi](https://twitter.com/koba04) for all his work on DevTools and docs– we couldn’t have done it without you!
\ No newline at end of file
diff --git a/pages/blog/swr-v2.es-ES.mdx b/pages/blog/swr-v2.es-ES.mdx
new file mode 100644
index 00000000..8d53655d
--- /dev/null
+++ b/pages/blog/swr-v2.es-ES.mdx
@@ -0,0 +1,410 @@
+---
+image: https://assets.vercel.com/image/upload/v1670542323/swr/v2.png
+description: 'Announcing SWR 2.0: new mutation APIs and improvd optimistic UI capabilities, new DevTools, better support for concurrent rendering, and more.'
+date: December 9th, 2022
+---
+
+import Callout from 'nextra-theme-docs/callout'
+import Bleed from 'nextra-theme-docs/bleed'
+
+import Authors, { Author } from 'components/authors'
+import Video from 'components/video'
+
+# Announcing SWR 2.0
+
+
+
+
+
+
+
+
+Today, we are excited to announce the release of SWR 2.0! This new version comes with a lot of improvements and new features: new mutation APIs and improvements to optimistic UI scenarios, new DevTools, better support for concurrent rendering, etc. We would like to give a big shout-out to all the contributors and maintainers who helped make this release possible.
+
+## Mutation and Optimistic UI
+
+### useSWRMutation
+
+Mutation is an important part of the data-fetching process. They allow you to make changes to your data both locally and remotely. Our existing `mutate` API allows you to revalidate and mutate resources manually. In SWR 2.0, the new hook `useSWRMutation` makes it even simpler to remotely change data using a declarative API. You can set up a mutation using the hook, and then activate it later:
+
+```jsx {11,16}
+import useSWRMutation from 'swr/mutation'
+
+async function sendRequest(url, { arg }) {
+ return fetch(url, {
+ method: 'POST',
+ body: JSON.stringify(arg)
+ })
+}
+
+function App() {
+ const { trigger, isMutating } = useSWRMutation('/api/user', sendRequest)
+
+ return (
+ trigger({ username: 'johndoe' })}
+ >{
+ isMutating ? 'Creating...' : 'Create User'
+ }
+ )
+}
+```
+
+The example above defines a `sendRequest` mutation that affects the `'/api/user'` resource. Unlike `useSWR`, `useSWRMutation` will not immediately start the request upon rendering. Instead, it returns a `trigger` function that can later be called to manually start the mutation.
+
+The `sendRequest` function will be called when the button is clicked, with the extra argument `{ username: 'johndoe' }`. The value of `isMutating` will be set to `true` until the mutation has finished.
+
+Additionally, this new hook addresses other issues you may have with mutations:
+
+- Optimistically update the UI while data is being mutated
+- Automatically revert when mutation fails
+- Avoid any potential race conditions between `useSWR` and other mutations of the same resource
+- Populate the `useSWR` cache after mutation completes
+- ...
+
+You can find in-depth API references and examples by reading the [docs](/docs/mutation#useswrmutation) or scrolling through the next few sections.
+
+### Optimistic UI
+
+Optimistic UI is an excellent model for creating websites that feel fast and responsive; however, it can be difficult to implement correctly. SWR 2.0 has added some new powerful options to make it easier.
+
+Let’s say we have an API that adds a new todo to the todo list and sends it to the server:
+
+```jsx
+await addNewTodo('New Item')
+```
+
+In our UI, we use a `useSWR` hook to display the todo list, with an “Add New Item” button that triggers this request and asks SWR to re-fetch the data via `mutate()`:
+
+```jsx {7,8}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ await addNewTodo('New Item')
+ mutate()
+ }}>
+ Add New Item
+
+>
+```
+
+However, the `await addNewTodo(...)` request could be very slow. When it’s ongoing, users still see the old list even if we can already know what the new list will look like. With the new `optimisticData` option, we can show the new list optimistically, before the server responds:
+
+```jsx {8}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+SWR will immediately update the `data` with the `optimisticData` value, and then send the request to the server. Once the request finishes, SWR will revalidate the resource to ensure it’s the latest.
+
+Like many APIs, if the `addNewTodo(...)` request returns us the latest data from the server, we can directly show that result, too (instead of starting a new revalidation)! There’s the new `populateCache` option to tell SWR to update the local data with the mutate response:
+
+```jsx {9}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ populateCache: true,
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+At the same time, we don’t need another revalidation afterward as the response data is from the source of truth, we can disable it with the `revalidate` option:
+
+```jsx {10}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ populateCache: true,
+ revalidate: false,
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+Lastly, if `addNewTodo(...)` fails with an exception, we can revert the optimistic data (`[...data, 'New Item']`) we just set, by setting `rollbackOnError` to `true` (which is also the default option). When that happens, SWR will roll back `data` to the previous value.
+
+```jsx {11}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ populateCache: true,
+ revalidate: false,
+ rollbackOnError: true,
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+All these APIs are supported in the new `useSWRMutation` hook as well. To learn more about them, you can check out our [docs](/docs/mutation#optimistic-updates). And here is a demo showing that behavior:
+
+
+
+### Mutate Multiple Keys
+
+The global `mutate` API now accepts a filter function, where you can mutate or revalidate specific keys. This will be helpful for use cases such as invalidating all the cached data. To learn more, you can read [Mutate Multiple Keys](/docs/mutation#mutate-multiple-items) in the docs.
+
+```jsx
+import { mutate } from 'swr'
+// Or from the hook if you have customized your cache provider:
+// { mutate } = useSWRConfig()
+
+// Mutate single resource
+mutate(key)
+
+// Mutate multiple resources and clear the cache (set to undefined)
+mutate(
+ key => typeof key === 'string' && key.startsWith('/api/item?id='),
+ undefined,
+ { revalidate: false }
+)
+```
+
+## SWR DevTools
+
+[SWRDevTools](https://swr-devtools.vercel.app) is a browser extension that helps you debug your SWR cache and the fetch results. Check our [devtools](/docs/advanced/devtools) section for how to use devtools in your application.
+
+![](/img/devtools/cache-view.jpg)
+
+## Preloading Data
+
+Preloading data can improve the user experience tremendously. If you know the resource is going to be used later in the application, you can use the new `preload` API to start fetching it early:
+
+```jsx {6}
+import useSWR, { preload } from 'swr'
+
+const fetcher = (url) => fetch(url).then((res) => res.json())
+
+// You can call the preload function in anywhere
+preload('/api/user', fetcher)
+
+function Profile() {
+ // The component that actually uses the data:
+ const { data, error } = useSWR('/api/user', fetcher)
+ // ...
+}
+
+export function Page () {
+ return
+}
+```
+
+In this example, the `preload` API is called in the global scope. This means that we start to preload the resource before React even starts to render anything.
+And when the `Profile` component is being rendered, the data can probably be available already. If it’s still ongoing, the `useSWR` hook will reuse that ongoing preloading request instead of starting a new one.
+
+The `preload` API can also be used in cases like preloading data for another page that will likely be rendered. More information about prefetching data with SWR can be found [here](/docs/prefetching).
+
+## `isLoading`
+
+`isLoading` is a new state returned by `useSWR`, that indicates **if the request is still ongoing, and there is no data loaded yet**. Previously, the `isValidating` state represents both the initial loading state and revalidating state so we had to check if both `data` and `error` are `undefined` to determine if it was the initial loading state.
+
+Now, it is so easy that you can directly use the `isLoading` value to render a loading message:
+
+```jsx
+import useSWR from 'swr'
+
+function Profile() {
+ const { data, isLoading } = useSWR('/api/user', fetcher)
+
+ if (isLoading) return loading...
+ return hello {data.name}!
+}
+```
+
+Note that `isValidating` is still present so you can still use it to show a loading indicator for revalidations.
+
+
+ We have added the new [Understanding SWR](/docs/advanced/understanding) page to describe how SWR returns values, which includes the difference between `isValidating` and `isLoading`, and how to combine them to improve user experience.
+
+
+## Preserving Previous State
+
+The `keepPreviousData` option is a new addition that allows you to keep the data that was fetched before. This improves UX immensely when you’re fetching data based on user actions happening in real time, like with a live search feature, where the resource’s `key` keeps changing:
+
+```jsx {5}
+function Search() {
+ const [search, setSearch] = React.useState('');
+
+ const { data, isLoading } = useSWR(`/search?q=${search}`, fetcher, {
+ keepPreviousData: true
+ })
+
+ return (
+
+
setSearch(e.target.value)}
+ placeholder="Search..."
+ />
+
+
+ {data?.products.map(item =>
)
+
+
+ );
+}
+```
+
+
+
+Check the code on [CodeSandbox](https://codesandbox.io/s/swr-keeppreviousdata-fsjz3m) and you can read more about it [here](/docs/advanced/understanding#return-previous-data-for-better-ux).
+
+## Extending Configurations
+
+`SWRConfig` can now accept a function value. When you have multiple levels of ``, the inner receives the parent configuration and returns a new one. This change makes it more flexible to configure SWR in a large codebase. More information can be found [here](/docs/global-configuration).
+
+```jsx
+ ({
+ dedupingInterval: parentConfig.dedupingInterval * 5,
+ refreshInterval: 100,
+ })}
+>
+
+
+```
+
+## Improved React 18 Support
+
+SWR has updated its internal code to use `useSyncExternalStore` and `startTransition` APIs in React 18. These ensure stronger consistency when rendering UI concurrently. This change doesn’t require any user code changes and all developers will benefit from it directly. Shims are included for React 17 and below.
+
+SWR 2.0 and all the new features are still compatible with React 16 and 17.
+
+## Migration Guide
+
+### Fetcher No Longer Accepts Multiple Arguments
+
+`key` is now passed as a single argument.
+
+```diff
+- useSWR([1, 2, 3], (a, b, c) => {
++ useSWR([1, 2, 3], ([a, b, c]) => {
+ assert(a === 1)
+ assert(b === 2)
+ assert(c === 3)
+})
+```
+
+### Global Mutate No Longer Accepts a `getKey` Function
+
+Now, if you pass a function to the global `mutate`, it will be used as a [filter](/blog/swr-v2#mutate-multiple-keys). Previously, you can pass a function that returns a key to the global `mutate`:
+
+```diff
+- mutate(() => '/api/item') // a function to return a key
++ mutate('/api/item') // to mutate the key, directly pass it
+```
+
+### New Required Property `keys()` for Cache Interface
+
+When you use your own cache implementation, the Cache interface now requires a `keys()` method that returns all keys in the cache object, similar to the JavaScript Map instances.
+
+```diff
+interface Cache {
+ get(key: string): Data | undefined
+ set(key: string, value: Data): void
+ delete(key: string): void
++ keys(): IterableIterator
+}
+```
+
+### Changed Cache Internal Structure
+
+The internal structure of the cache data will be an object that holds all the current states.
+
+```diff
+- assert(cache.get(key) === data)
++ assert(cache.get(key) === { data, error, isValidating })
+
+// getter
+- cache.get(key)
++ cache.get(key)?.data
+
+// setter
+- cache.set(key, data)
++ cache.set(key, { ...cache.get(key), data })
+```
+
+
+ You should not write to the cache directly, it might cause undefined behavior.
+
+
+### `SWRConfig.default` Is Renamed as `SWRConfig.defaultValue`
+
+`SWRConfig.defaultValue` is the property for accessing the default SWR config.
+
+```diff
+- SWRConfig.default
++ SWRConfig.defaultValue
+```
+
+### Type `InfiniteFetcher` Is Renamed as `SWRInfiniteFetcher`
+
+```diff
+- import type { InfiniteFetcher } from 'swr/infinite'
++ import type { SWRInfiniteFetcher } from 'swr/infinite'
+```
+
+### Avoid Suspense on Server
+
+If you want to use `suspense: true` with SWR on the server-side, including pre-rendering in Next.js, then you must provide initial data via [`fallbackData` or `fallback`](/docs/with-nextjs#pre-rendering-with-default-data). Today, this means that you can't use Suspense to fetch data on the server side. Your other two options are doing fully client-side data-fetching or getting your framework to fetch the data for you (like getStaticProps does in Next.js).
+
+### ES2018 as the Build Target
+
+If you want to support IE 11, you have to target ES5 in your framework or a bundler. This change has made a performance improvement on SSR, and keeps the bundle size small.
+
+## Changelog
+
+Read the full Changelog [on GitHub](https://github.com/vercel/swr/releases).
+
+## The Future & Thank You!
+
+With the new release of [Next.js 13](https://nextjs.org/blog/next-13), we see a lot of exciting new things as well as paradigm shifts in the React ecosystem: [React Server Components](https://beta.nextjs.org/docs/rendering/server-and-client-components), streaming SSR, [async components](https://beta.nextjs.org/docs/data-fetching/fetching#asyncawait-in-server-components), and the [`use` hook](https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md#usepromise). Many of them are related to data-fetching, and some of them have overlapping use cases with SWR.
+
+However, the goal of the SWR project remains the same. We want it to be a drop-in library that is lightweight, framework agnostic, and a little bit _opinionated_ (i.e. revalidate upon focus). Instead of trying to be a standard solution, we want to focus on innovations that make the UX better. In the meantime, we are also doing research on how to improve SWR with these new abilities of React.
+
+We want to thank every one of the [143](https://github.com/vercel/swr/graphs/contributors) contributors (+ [106](https://github.com/vercel/swr-site/graphs/contributors) docs contributors), as well as those who helps us out or gave feedback. A special thanks goes to [Toru Kobayashi](https://twitter.com/koba04) for all his work on DevTools and docs– we couldn’t have done it without you!
\ No newline at end of file
diff --git a/pages/blog/swr-v2.ja.mdx b/pages/blog/swr-v2.ja.mdx
new file mode 100644
index 00000000..67f87b57
--- /dev/null
+++ b/pages/blog/swr-v2.ja.mdx
@@ -0,0 +1,412 @@
+---
+image: https://assets.vercel.com/image/upload/v1670542323/swr/v2.png
+description: 'Announcing SWR 2.0: new mutation APIs and improvd optimistic UI capabilities, new DevTools, better support for concurrent rendering, and more.'
+date: December 9th, 2022
+---
+
+import Callout from 'nextra-theme-docs/callout'
+import Bleed from 'nextra-theme-docs/bleed'
+
+import Authors, { Author } from 'components/authors'
+import Video from 'components/video'
+
+# SWR 2.0 の発表
+
+
+
+
+
+
+
+
+本日、SWR 2.0 のリリースを発表できることに興奮しています!この新しいバージョンには、新しいミューテーション API や楽観的更新パターンに対する改善、DevTools、React の並行処理機能のサポートといった多くの改善と新しい機能が含まれています。このリリースを可能にしてくれた全てのコントリビュータとメンテナに感謝しています。
+
+## ミューテーションと楽観的 UI 更新
+
+### useSWRMutation
+
+ミューテーションはデータフェッチングの重要な要素の一つです。ミューテーションはローカルとリモートのデータそれぞれの更新を可能にします。既存の `mutate` API は、リソースの再検証及び手動による更新をサポートしています。SWR 2.0 では、`useSWRMutation` という新しいフックは宣言的な API でよりシンプルにリモートのデータ更新を可能にします。このフックを使いミューテーションをセットアップし、その後利用できます。
+
+```jsx {11,16}
+import useSWRMutation from 'swr/mutation'
+
+async function sendRequest(url, { arg }) {
+ return fetch(url, {
+ method: 'POST',
+ body: JSON.stringify(arg)
+ })
+}
+
+function App() {
+ const { trigger, isMutating } = useSWRMutation('/api/user', sendRequest)
+
+ return (
+ trigger({ username: 'johndoe' })}
+ >{
+ isMutating ? 'Creating...' : 'Create User'
+ }
+ )
+}
+```
+
+上記の例では、`sendRequest` という `'/api/user'` のリソースに対して影響を与えるミューテーションを定義しています。`useSWR` と違い、`useSWRMutation` はレンダリング中に即座にリクエストを開始しません。代わりに、後ほど手動でミューテーションを開始するための `trigger` 関数を返します。
+
+`sendRequest` 関数はボタンがクリックされた時に追加の引数 `{ username: 'johndoe' }` と一緒に呼ばれます。`isMutating` の値はミューテーションが終了するまでの間、`true` に設定されます。
+
+加えて、新しいフックはその他のミューテーションに対する課題もカバーします。
+
+- ミューテーション中の楽観的な UI の更新
+- ミューテーションが失敗した際には自動的に変更を取り消す
+- 同じリソースに対する `useSWR` や他のミューテーションとのレースコンディションを避ける
+- ミューテーションが完了した後に `useSWR` のキャッシュにデータを反映する
+- ...
+
+この API に対する詳細な参照や例は [ドキュメント](/docs/mutation#useswrmutation) または以降のセクションまでスクロールすることで確認できます。
+
+### 楽観的 UI 更新
+
+楽観的 UI 更新は速く反応がいいウェブサイトを作るための素晴らしいモデルです。しかしながら正しく実装することは難しいです。SWR 2.0 ではこれを簡単にするためのオプションを追加しました。
+
+Todo リストに新しい Todo を追加するための API があり、それを用いてサーバにデータを送信する場合を見てみましょう。
+
+```jsx
+await addNewTodo('New Item')
+```
+
+この UI では、`useSWR` フックを Todo リストを表示するために使っており、`mutate()` を通じてサーバにリクエストを送信して SWR に再フェッチを依頼するための “Add New Item” ボタンがあります。
+
+```jsx {7,8}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ await addNewTodo('New Item')
+ mutate()
+ }}>
+ Add New Item
+
+>
+```
+
+しかしながら、`await addNewTodo(...)` によるリクエストに時間がかかる場合があります。リクエストを実行中、ユーザーは新しいリストがどうなるか知っているにも関わらず古い状態のリストを見続けることになります。新しい `optimisticData` オプションを使うことで、サーバがレスポンスを返す前に新しい状態のリストを楽観的 UI として表示できます。
+
+```jsx {8}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+SWR は即座に `data` を `optimisticData` の値によって更新し、サーバにリクエストを送信します。リクエストが完了したら SWR はリソースを再検証しデータが最新であることを保証します。
+
+他の多くの API のように、`addNewTodo(...)` が最新の結果をサーバから返す場合、私達はその結果を(再検証を行うことなしに)そのまま表示することもできます。新しい `populateCache` オプションは SWR にミューテーションのレスポンスでローカルデータを更新することを伝えるオプションです。
+
+```jsx {9}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ populateCache: true,
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+同時に、レスポンスデータが正しいデータである場合には再検証が必要ありません。`revalidate` オプションで再検証を無効化できます。
+
+```jsx {10}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ populateCache: true,
+ revalidate: false,
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+最後に、もし `addNewTodo(...)` が例外で失敗した場合、`rollbackOnError` オプションを `true` (デフォルトのオプション) に設定することで楽観的更新としてセットしたデータ (`[...data, 'New Item']`) を取り消すことができます。それが起きた時、SWR は `data` の更新前のデータにロールバックします。
+
+```jsx {11}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ populateCache: true,
+ revalidate: false,
+ rollbackOnError: true,
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+これらの全ての API は新しい `useSWRMutation` でも同様にサポートされています。更なる情報は [ドキュメント](/docs/mutation#optimistic-updates) で確認できます。下記はこの挙動を示したデモです。
+
+
+
+### 複数キーのミューテーション
+
+グローバルの `mutate` API がフィルター関数を受け取るようになり、特定のキーに対するミューテーションが可能になります。これは全てのキャッシュデータを無効化するようなユースケースで便利です。詳細はドキュメントの [複数のキーをミューテーションする](/docs/mutation#mutate-multiple-items) で確認できます。
+
+```jsx
+import { mutate } from 'swr'
+// またはキャッシュプロバイダをカスタマイズしている場合には、フックから
+// { mutate } = useSWRConfig()
+
+// 単一のリソースをミューテートする
+mutate(key)
+
+// 複数のリソースをミューテートしてキャッシュをクリアする(undefined をセットする)
+mutate(
+ key => typeof key === 'string' && key.startsWith('/api/item?id='),
+ undefined,
+ { revalidate: false }
+)
+```
+
+## SWR DevTools
+
+[SWRDevTools](https://swr-devtools.vercel.app) はブラウザ拡張で SWR のキャッシュやフェッチの結果をデバッグするのに役に立ちます。アプリケーションでの使い方は [開発者ツール](/docs/advanced/devtools) セクションを確認ください。
+
+![](/img/devtools/cache-view.jpg)
+
+## データのプリロード
+
+データのプリロードはユーザー体験を劇的に改善します。もしリソースが後ほど使われることがわかっている場合、`preload` API を使うことでフェッチの開始を事前に行うことができます。
+
+```jsx {6}
+import useSWR, { preload } from 'swr'
+
+const fetcher = (url) => fetch(url).then((res) => res.json())
+
+// preload 関数をどこでも呼べます
+preload('/api/user', fetcher)
+
+function Profile() {
+ // 実際のコンポーネントの中でデータを使う箇所
+ const { data, error } = useSWR('/api/user', fetcher)
+ // ...
+}
+
+export function Page () {
+ return
+}
+```
+
+この例では、`preload` API はグローバルスコープ内で呼ばれています。これは、リソースのプリロードを React がレンダリングを開始するより前に行なっていることを意味します。
+それにより `Profile` コンポーネントがレンダリングされた時、データはすでに利用可能になっているでしょう。もしリクエストが実行中だった場合、`useSWR` フックは新しいリクエストを開始するのではなく実行中のプリロードのリクエストを再利用します。
+
+`preload` API は後に表示されそうなページのデータをプリロードするためにも利用できます。SWR を使ったプリフェッチに関する更なる情報は [こちら](/docs/prefetching) より確認できます。
+
+## `isLoading`
+
+`isLoading` は `useSWR` から返される新しい状態で、**リクエストが実行中で、ロードされたデータがない** ことを示します。これまでは、`isValidating` は初期ローディングと再検証の状態のどちらも表していたため、初期ローディングかどうかを知るためには `data` と `error` が `undefined` であるかどうかをチェックする必要がありました。
+
+今は、`isLoading` をそのまま使うことで簡単にローディングメッセージの表示ができます。
+
+```jsx
+import useSWR from 'swr'
+
+function Profile() {
+ const { data, isLoading } = useSWR('/api/user', fetcher)
+
+ if (isLoading) return loading...
+ return hello {data.name}!
+}
+```
+
+引き続き、`isValidating` は提供されており、再検証時のローディングを出すために利用可能な点に注意してください。
+
+
+ 今回、SWR がどのように値を返すかを示した新しい [SWR を理解する](/docs/advanced/understanding) ページを追加しました。このページでは `isValidating` と `isLoading` の違いやこれらを組み合わせてどのようにユーザー体験を向上させるかが解説されています。
+
+
+## 以前の状態を保持する
+
+`keepPreviousData` は新しく追加されたオプションで、以前にフェッチされたデータを保持します。これは、リアルタイム検索のようにリソースの `key` が変化し続けユーザーアクションに応じて都度データのフェッチが発生するようなケースで UX を劇的に向上させます。
+
+```jsx {5}
+function Search() {
+ const [search, setSearch] = React.useState('');
+
+ const { data, isLoading } = useSWR(`/search?q=${search}`, fetcher, {
+ keepPreviousData: true
+ })
+
+ return (
+
+
setSearch(e.target.value)}
+ placeholder="Search..."
+ />
+
+
+ {data?.products.map(item =>
)
+
+
+ );
+}
+```
+
+
+
+詳しくは [CodeSandbox](https://codesandbox.io/s/swr-keeppreviousdata-fsjz3m) のコードと [ドキュメント](/docs/advanced/understanding#return-previous-data-for-better-ux) を確認ください。
+
+## 設定の拡張
+
+`SWRConfig` は関数を値として受け取るようになりました。複数レベルの `` を持っている場合、内部のコンポーネントは親の設定を受け取り、新しい設定を返します。これは、大規模なコードベースにおいて柔軟な SWR の設定を可能にします。詳細は [ドキュメント](/docs/global-configuration) を確認ください。
+
+```jsx
+ ({
+ dedupingInterval: parentConfig.dedupingInterval * 5,
+ refreshInterval: 100,
+ })}
+>
+
+
+```
+
+## React 18 サポートの改善
+
+SWR は内部実装において `useSyncExternalStore` や `startTransition` といった React 18 で追加された API を使うようになりました。これらは UI のレンダリングが並行処理になった場合においても一貫性を保証してくれます。これにより利用者の実装を変更する必要はなく、全ての開発者が恩恵を受けることができます。React 17 や古いバージョンのための実装も含まれています。
+
+SWR 2.0 と全ての新しい機能は引き続き、React 16 や 17 に対して互換性があります。
+
+## 移行ガイド
+
+### フェッチャーが引数を複数の引数として受け取らなくなります
+
+`key` は単一の引数として渡されるようになります。
+
+```diff
+- useSWR([1, 2, 3], (a, b, c) => {
++ useSWR([1, 2, 3], ([a, b, c]) => {
+ assert(a === 1)
+ assert(b === 2)
+ assert(c === 3)
+})
+```
+
+### グローバルのミューテーション API が `getKey` 関数を受け取らなくなります
+
+もしグローバルな `mutate` に対して関数を渡した場合、それは [フィルター関数](/blog/swr-v2#mutate-multiple-keys) として使われます。これまでは、グローバルの `mutate` に対してキーを返す関数を渡すことができました。
+
+
+```diff
+- mutate(() => '/api/item') // キーを返す関数
++ mutate('/api/item') // 直接キーを渡すようにする
+```
+
+### キャッシュのインターフェイスで `keys()` が必須になりました
+
+独自のキャッシュ実装を使っている場合、キャッシュのインターフェイスとして JavaScript の Map のようにキャッシュの全てのキーを返す `keys()` メソッドが必須になりました。
+
+```diff
+interface Cache {
+ get(key: string): Data | undefined
+ set(key: string, value: Data): void
+ delete(key: string): void
++ keys(): IterableIterator
+}
+```
+
+### キャッシュの内部構造を変更しました
+
+キャッシュの内部構造が、全ての状態を保持する単一のオブジェクトになりました。
+
+```diff
+- assert(cache.get(key) === data)
++ assert(cache.get(key) === { data, error, isValidating })
+
+// getter
+- cache.get(key)
++ cache.get(key)?.data
+
+// setter
+- cache.set(key, data)
++ cache.set(key, { ...cache.get(key), data })
+```
+
+
+ キャッシュに対して直接データを書き込むことはすべきではありません。予期せぬ挙動を引き起こす可能性があります。
+
+
+### `SWRConfig.default` が `SWRConfig.defaultValue` に変更されました
+
+`SWRConfig.defaultValue` はデフォルトの SWR の設定にアクセスするためのプロパティです。
+
+```diff
+- SWRConfig.default
++ SWRConfig.defaultValue
+```
+
+### `InfiniteFetcher` の型が `SWRInfiniteFetcher` に変更されました
+
+```diff
+- import type { InfiniteFetcher } from 'swr/infinite'
++ import type { SWRInfiniteFetcher } from 'swr/infinite'
+```
+
+### サーバサイドでのサスペンスを用いたデータフェッチを避ける
+
+SWR を `suspense: true` と一緒にサーバーサイドで使いたい場合には (Next.js によるプリレンダリングも含みます)、[`fallbackData` or `fallback`](/docs/with-nextjs#pre-rendering-with-default-data) により初期データが必ず提供されている必要があります。これは現時点でサスペンスを使ったデータ取得をサーバーサイドで行えないことを意味します。その他の 2 つのオプションは完全にクライアントサイドでデータ取得を行うかフレームワークを用いたデータの取得 (Next.js が getStaticProps でやっているような) を行うかです。
+
+### ES2018 がビルドターゲットに
+
+IE 11 をサポートしたい場合、フレームワークやバンドラで ES5 をビルドターゲットにする必要があります。この変更により SSR 時のパフォーマンス改善やバンドルサイズ削減を実現しました。
+
+## 変更ログ
+
+完全な変更ログを [GitHub 上で](https://github.com/vercel/swr/releases) 確認できます。
+
+## 今後と感謝
+
+新しい [Next.js 13](https://nextjs.org/blog/next-13) のリリースにおいては、多くのエキサイティングな新機能だけでなく、[React Server Components](https://beta.nextjs.org/docs/rendering/server-and-client-components) やストリーミング SSR、[非同期コンポーネント](https://beta.nextjs.org/docs/data-fetching/fetching#asyncawait-in-server-components)、[`use` フック](https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md#usepromise) といった React のエコシステムにおけるパラダイムシフトがありました。それらの多くはデータ取得に関するものであり、いくつかは SWR のユースケースとも被るものです。
+
+しかしながら、SWR プロジェクトのゴールは引き続き変わらないままです。私たちは軽量に導入可能で、フレームワークに依存せず、少し _こだわりのある_ (例: フォーカス時の再検証) を目指しています。標準的な手法になるのを目指す代わりに、よりよい UX を実現するためイノベーションにフォーカスしたいと考えています。その一方で私たちは React の新しい力を用いてどのように SWR を改善できるのかを調査しています。
+
+
+[143](https://github.com/vercel/swr/graphs/contributors) 人のコントリビュータ (+ [106](https://github.com/vercel/swr-site/graphs/contributors) のドキュメントコントリビュータ)、そしてご協力いただいた皆様、フィードバックをいただいた皆様にも感謝しています。[Toru Kobayashi](https://twitter.com/koba04) さんの DevTools やドキュメントに対する貢献には特に感謝しています。彼の貢献なしにはこのリリースは実現できませんでした!
\ No newline at end of file
diff --git a/pages/blog/swr-v2.ko.mdx b/pages/blog/swr-v2.ko.mdx
new file mode 100644
index 00000000..8d53655d
--- /dev/null
+++ b/pages/blog/swr-v2.ko.mdx
@@ -0,0 +1,410 @@
+---
+image: https://assets.vercel.com/image/upload/v1670542323/swr/v2.png
+description: 'Announcing SWR 2.0: new mutation APIs and improvd optimistic UI capabilities, new DevTools, better support for concurrent rendering, and more.'
+date: December 9th, 2022
+---
+
+import Callout from 'nextra-theme-docs/callout'
+import Bleed from 'nextra-theme-docs/bleed'
+
+import Authors, { Author } from 'components/authors'
+import Video from 'components/video'
+
+# Announcing SWR 2.0
+
+
+
+
+
+
+
+
+Today, we are excited to announce the release of SWR 2.0! This new version comes with a lot of improvements and new features: new mutation APIs and improvements to optimistic UI scenarios, new DevTools, better support for concurrent rendering, etc. We would like to give a big shout-out to all the contributors and maintainers who helped make this release possible.
+
+## Mutation and Optimistic UI
+
+### useSWRMutation
+
+Mutation is an important part of the data-fetching process. They allow you to make changes to your data both locally and remotely. Our existing `mutate` API allows you to revalidate and mutate resources manually. In SWR 2.0, the new hook `useSWRMutation` makes it even simpler to remotely change data using a declarative API. You can set up a mutation using the hook, and then activate it later:
+
+```jsx {11,16}
+import useSWRMutation from 'swr/mutation'
+
+async function sendRequest(url, { arg }) {
+ return fetch(url, {
+ method: 'POST',
+ body: JSON.stringify(arg)
+ })
+}
+
+function App() {
+ const { trigger, isMutating } = useSWRMutation('/api/user', sendRequest)
+
+ return (
+ trigger({ username: 'johndoe' })}
+ >{
+ isMutating ? 'Creating...' : 'Create User'
+ }
+ )
+}
+```
+
+The example above defines a `sendRequest` mutation that affects the `'/api/user'` resource. Unlike `useSWR`, `useSWRMutation` will not immediately start the request upon rendering. Instead, it returns a `trigger` function that can later be called to manually start the mutation.
+
+The `sendRequest` function will be called when the button is clicked, with the extra argument `{ username: 'johndoe' }`. The value of `isMutating` will be set to `true` until the mutation has finished.
+
+Additionally, this new hook addresses other issues you may have with mutations:
+
+- Optimistically update the UI while data is being mutated
+- Automatically revert when mutation fails
+- Avoid any potential race conditions between `useSWR` and other mutations of the same resource
+- Populate the `useSWR` cache after mutation completes
+- ...
+
+You can find in-depth API references and examples by reading the [docs](/docs/mutation#useswrmutation) or scrolling through the next few sections.
+
+### Optimistic UI
+
+Optimistic UI is an excellent model for creating websites that feel fast and responsive; however, it can be difficult to implement correctly. SWR 2.0 has added some new powerful options to make it easier.
+
+Let’s say we have an API that adds a new todo to the todo list and sends it to the server:
+
+```jsx
+await addNewTodo('New Item')
+```
+
+In our UI, we use a `useSWR` hook to display the todo list, with an “Add New Item” button that triggers this request and asks SWR to re-fetch the data via `mutate()`:
+
+```jsx {7,8}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ await addNewTodo('New Item')
+ mutate()
+ }}>
+ Add New Item
+
+>
+```
+
+However, the `await addNewTodo(...)` request could be very slow. When it’s ongoing, users still see the old list even if we can already know what the new list will look like. With the new `optimisticData` option, we can show the new list optimistically, before the server responds:
+
+```jsx {8}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+SWR will immediately update the `data` with the `optimisticData` value, and then send the request to the server. Once the request finishes, SWR will revalidate the resource to ensure it’s the latest.
+
+Like many APIs, if the `addNewTodo(...)` request returns us the latest data from the server, we can directly show that result, too (instead of starting a new revalidation)! There’s the new `populateCache` option to tell SWR to update the local data with the mutate response:
+
+```jsx {9}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ populateCache: true,
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+At the same time, we don’t need another revalidation afterward as the response data is from the source of truth, we can disable it with the `revalidate` option:
+
+```jsx {10}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ populateCache: true,
+ revalidate: false,
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+Lastly, if `addNewTodo(...)` fails with an exception, we can revert the optimistic data (`[...data, 'New Item']`) we just set, by setting `rollbackOnError` to `true` (which is also the default option). When that happens, SWR will roll back `data` to the previous value.
+
+```jsx {11}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ populateCache: true,
+ revalidate: false,
+ rollbackOnError: true,
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+All these APIs are supported in the new `useSWRMutation` hook as well. To learn more about them, you can check out our [docs](/docs/mutation#optimistic-updates). And here is a demo showing that behavior:
+
+
+
+### Mutate Multiple Keys
+
+The global `mutate` API now accepts a filter function, where you can mutate or revalidate specific keys. This will be helpful for use cases such as invalidating all the cached data. To learn more, you can read [Mutate Multiple Keys](/docs/mutation#mutate-multiple-items) in the docs.
+
+```jsx
+import { mutate } from 'swr'
+// Or from the hook if you have customized your cache provider:
+// { mutate } = useSWRConfig()
+
+// Mutate single resource
+mutate(key)
+
+// Mutate multiple resources and clear the cache (set to undefined)
+mutate(
+ key => typeof key === 'string' && key.startsWith('/api/item?id='),
+ undefined,
+ { revalidate: false }
+)
+```
+
+## SWR DevTools
+
+[SWRDevTools](https://swr-devtools.vercel.app) is a browser extension that helps you debug your SWR cache and the fetch results. Check our [devtools](/docs/advanced/devtools) section for how to use devtools in your application.
+
+![](/img/devtools/cache-view.jpg)
+
+## Preloading Data
+
+Preloading data can improve the user experience tremendously. If you know the resource is going to be used later in the application, you can use the new `preload` API to start fetching it early:
+
+```jsx {6}
+import useSWR, { preload } from 'swr'
+
+const fetcher = (url) => fetch(url).then((res) => res.json())
+
+// You can call the preload function in anywhere
+preload('/api/user', fetcher)
+
+function Profile() {
+ // The component that actually uses the data:
+ const { data, error } = useSWR('/api/user', fetcher)
+ // ...
+}
+
+export function Page () {
+ return
+}
+```
+
+In this example, the `preload` API is called in the global scope. This means that we start to preload the resource before React even starts to render anything.
+And when the `Profile` component is being rendered, the data can probably be available already. If it’s still ongoing, the `useSWR` hook will reuse that ongoing preloading request instead of starting a new one.
+
+The `preload` API can also be used in cases like preloading data for another page that will likely be rendered. More information about prefetching data with SWR can be found [here](/docs/prefetching).
+
+## `isLoading`
+
+`isLoading` is a new state returned by `useSWR`, that indicates **if the request is still ongoing, and there is no data loaded yet**. Previously, the `isValidating` state represents both the initial loading state and revalidating state so we had to check if both `data` and `error` are `undefined` to determine if it was the initial loading state.
+
+Now, it is so easy that you can directly use the `isLoading` value to render a loading message:
+
+```jsx
+import useSWR from 'swr'
+
+function Profile() {
+ const { data, isLoading } = useSWR('/api/user', fetcher)
+
+ if (isLoading) return loading...
+ return hello {data.name}!
+}
+```
+
+Note that `isValidating` is still present so you can still use it to show a loading indicator for revalidations.
+
+
+ We have added the new [Understanding SWR](/docs/advanced/understanding) page to describe how SWR returns values, which includes the difference between `isValidating` and `isLoading`, and how to combine them to improve user experience.
+
+
+## Preserving Previous State
+
+The `keepPreviousData` option is a new addition that allows you to keep the data that was fetched before. This improves UX immensely when you’re fetching data based on user actions happening in real time, like with a live search feature, where the resource’s `key` keeps changing:
+
+```jsx {5}
+function Search() {
+ const [search, setSearch] = React.useState('');
+
+ const { data, isLoading } = useSWR(`/search?q=${search}`, fetcher, {
+ keepPreviousData: true
+ })
+
+ return (
+
+
setSearch(e.target.value)}
+ placeholder="Search..."
+ />
+
+
+ {data?.products.map(item =>
)
+
+
+ );
+}
+```
+
+
+
+Check the code on [CodeSandbox](https://codesandbox.io/s/swr-keeppreviousdata-fsjz3m) and you can read more about it [here](/docs/advanced/understanding#return-previous-data-for-better-ux).
+
+## Extending Configurations
+
+`SWRConfig` can now accept a function value. When you have multiple levels of ``, the inner receives the parent configuration and returns a new one. This change makes it more flexible to configure SWR in a large codebase. More information can be found [here](/docs/global-configuration).
+
+```jsx
+ ({
+ dedupingInterval: parentConfig.dedupingInterval * 5,
+ refreshInterval: 100,
+ })}
+>
+
+
+```
+
+## Improved React 18 Support
+
+SWR has updated its internal code to use `useSyncExternalStore` and `startTransition` APIs in React 18. These ensure stronger consistency when rendering UI concurrently. This change doesn’t require any user code changes and all developers will benefit from it directly. Shims are included for React 17 and below.
+
+SWR 2.0 and all the new features are still compatible with React 16 and 17.
+
+## Migration Guide
+
+### Fetcher No Longer Accepts Multiple Arguments
+
+`key` is now passed as a single argument.
+
+```diff
+- useSWR([1, 2, 3], (a, b, c) => {
++ useSWR([1, 2, 3], ([a, b, c]) => {
+ assert(a === 1)
+ assert(b === 2)
+ assert(c === 3)
+})
+```
+
+### Global Mutate No Longer Accepts a `getKey` Function
+
+Now, if you pass a function to the global `mutate`, it will be used as a [filter](/blog/swr-v2#mutate-multiple-keys). Previously, you can pass a function that returns a key to the global `mutate`:
+
+```diff
+- mutate(() => '/api/item') // a function to return a key
++ mutate('/api/item') // to mutate the key, directly pass it
+```
+
+### New Required Property `keys()` for Cache Interface
+
+When you use your own cache implementation, the Cache interface now requires a `keys()` method that returns all keys in the cache object, similar to the JavaScript Map instances.
+
+```diff
+interface Cache {
+ get(key: string): Data | undefined
+ set(key: string, value: Data): void
+ delete(key: string): void
++ keys(): IterableIterator
+}
+```
+
+### Changed Cache Internal Structure
+
+The internal structure of the cache data will be an object that holds all the current states.
+
+```diff
+- assert(cache.get(key) === data)
++ assert(cache.get(key) === { data, error, isValidating })
+
+// getter
+- cache.get(key)
++ cache.get(key)?.data
+
+// setter
+- cache.set(key, data)
++ cache.set(key, { ...cache.get(key), data })
+```
+
+
+ You should not write to the cache directly, it might cause undefined behavior.
+
+
+### `SWRConfig.default` Is Renamed as `SWRConfig.defaultValue`
+
+`SWRConfig.defaultValue` is the property for accessing the default SWR config.
+
+```diff
+- SWRConfig.default
++ SWRConfig.defaultValue
+```
+
+### Type `InfiniteFetcher` Is Renamed as `SWRInfiniteFetcher`
+
+```diff
+- import type { InfiniteFetcher } from 'swr/infinite'
++ import type { SWRInfiniteFetcher } from 'swr/infinite'
+```
+
+### Avoid Suspense on Server
+
+If you want to use `suspense: true` with SWR on the server-side, including pre-rendering in Next.js, then you must provide initial data via [`fallbackData` or `fallback`](/docs/with-nextjs#pre-rendering-with-default-data). Today, this means that you can't use Suspense to fetch data on the server side. Your other two options are doing fully client-side data-fetching or getting your framework to fetch the data for you (like getStaticProps does in Next.js).
+
+### ES2018 as the Build Target
+
+If you want to support IE 11, you have to target ES5 in your framework or a bundler. This change has made a performance improvement on SSR, and keeps the bundle size small.
+
+## Changelog
+
+Read the full Changelog [on GitHub](https://github.com/vercel/swr/releases).
+
+## The Future & Thank You!
+
+With the new release of [Next.js 13](https://nextjs.org/blog/next-13), we see a lot of exciting new things as well as paradigm shifts in the React ecosystem: [React Server Components](https://beta.nextjs.org/docs/rendering/server-and-client-components), streaming SSR, [async components](https://beta.nextjs.org/docs/data-fetching/fetching#asyncawait-in-server-components), and the [`use` hook](https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md#usepromise). Many of them are related to data-fetching, and some of them have overlapping use cases with SWR.
+
+However, the goal of the SWR project remains the same. We want it to be a drop-in library that is lightweight, framework agnostic, and a little bit _opinionated_ (i.e. revalidate upon focus). Instead of trying to be a standard solution, we want to focus on innovations that make the UX better. In the meantime, we are also doing research on how to improve SWR with these new abilities of React.
+
+We want to thank every one of the [143](https://github.com/vercel/swr/graphs/contributors) contributors (+ [106](https://github.com/vercel/swr-site/graphs/contributors) docs contributors), as well as those who helps us out or gave feedback. A special thanks goes to [Toru Kobayashi](https://twitter.com/koba04) for all his work on DevTools and docs– we couldn’t have done it without you!
\ No newline at end of file
diff --git a/pages/blog/swr-v2.pt-BR.mdx b/pages/blog/swr-v2.pt-BR.mdx
new file mode 100644
index 00000000..8d53655d
--- /dev/null
+++ b/pages/blog/swr-v2.pt-BR.mdx
@@ -0,0 +1,410 @@
+---
+image: https://assets.vercel.com/image/upload/v1670542323/swr/v2.png
+description: 'Announcing SWR 2.0: new mutation APIs and improvd optimistic UI capabilities, new DevTools, better support for concurrent rendering, and more.'
+date: December 9th, 2022
+---
+
+import Callout from 'nextra-theme-docs/callout'
+import Bleed from 'nextra-theme-docs/bleed'
+
+import Authors, { Author } from 'components/authors'
+import Video from 'components/video'
+
+# Announcing SWR 2.0
+
+
+
+
+
+
+
+
+Today, we are excited to announce the release of SWR 2.0! This new version comes with a lot of improvements and new features: new mutation APIs and improvements to optimistic UI scenarios, new DevTools, better support for concurrent rendering, etc. We would like to give a big shout-out to all the contributors and maintainers who helped make this release possible.
+
+## Mutation and Optimistic UI
+
+### useSWRMutation
+
+Mutation is an important part of the data-fetching process. They allow you to make changes to your data both locally and remotely. Our existing `mutate` API allows you to revalidate and mutate resources manually. In SWR 2.0, the new hook `useSWRMutation` makes it even simpler to remotely change data using a declarative API. You can set up a mutation using the hook, and then activate it later:
+
+```jsx {11,16}
+import useSWRMutation from 'swr/mutation'
+
+async function sendRequest(url, { arg }) {
+ return fetch(url, {
+ method: 'POST',
+ body: JSON.stringify(arg)
+ })
+}
+
+function App() {
+ const { trigger, isMutating } = useSWRMutation('/api/user', sendRequest)
+
+ return (
+ trigger({ username: 'johndoe' })}
+ >{
+ isMutating ? 'Creating...' : 'Create User'
+ }
+ )
+}
+```
+
+The example above defines a `sendRequest` mutation that affects the `'/api/user'` resource. Unlike `useSWR`, `useSWRMutation` will not immediately start the request upon rendering. Instead, it returns a `trigger` function that can later be called to manually start the mutation.
+
+The `sendRequest` function will be called when the button is clicked, with the extra argument `{ username: 'johndoe' }`. The value of `isMutating` will be set to `true` until the mutation has finished.
+
+Additionally, this new hook addresses other issues you may have with mutations:
+
+- Optimistically update the UI while data is being mutated
+- Automatically revert when mutation fails
+- Avoid any potential race conditions between `useSWR` and other mutations of the same resource
+- Populate the `useSWR` cache after mutation completes
+- ...
+
+You can find in-depth API references and examples by reading the [docs](/docs/mutation#useswrmutation) or scrolling through the next few sections.
+
+### Optimistic UI
+
+Optimistic UI is an excellent model for creating websites that feel fast and responsive; however, it can be difficult to implement correctly. SWR 2.0 has added some new powerful options to make it easier.
+
+Let’s say we have an API that adds a new todo to the todo list and sends it to the server:
+
+```jsx
+await addNewTodo('New Item')
+```
+
+In our UI, we use a `useSWR` hook to display the todo list, with an “Add New Item” button that triggers this request and asks SWR to re-fetch the data via `mutate()`:
+
+```jsx {7,8}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ await addNewTodo('New Item')
+ mutate()
+ }}>
+ Add New Item
+
+>
+```
+
+However, the `await addNewTodo(...)` request could be very slow. When it’s ongoing, users still see the old list even if we can already know what the new list will look like. With the new `optimisticData` option, we can show the new list optimistically, before the server responds:
+
+```jsx {8}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+SWR will immediately update the `data` with the `optimisticData` value, and then send the request to the server. Once the request finishes, SWR will revalidate the resource to ensure it’s the latest.
+
+Like many APIs, if the `addNewTodo(...)` request returns us the latest data from the server, we can directly show that result, too (instead of starting a new revalidation)! There’s the new `populateCache` option to tell SWR to update the local data with the mutate response:
+
+```jsx {9}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ populateCache: true,
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+At the same time, we don’t need another revalidation afterward as the response data is from the source of truth, we can disable it with the `revalidate` option:
+
+```jsx {10}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ populateCache: true,
+ revalidate: false,
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+Lastly, if `addNewTodo(...)` fails with an exception, we can revert the optimistic data (`[...data, 'New Item']`) we just set, by setting `rollbackOnError` to `true` (which is also the default option). When that happens, SWR will roll back `data` to the previous value.
+
+```jsx {11}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ populateCache: true,
+ revalidate: false,
+ rollbackOnError: true,
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+All these APIs are supported in the new `useSWRMutation` hook as well. To learn more about them, you can check out our [docs](/docs/mutation#optimistic-updates). And here is a demo showing that behavior:
+
+
+
+### Mutate Multiple Keys
+
+The global `mutate` API now accepts a filter function, where you can mutate or revalidate specific keys. This will be helpful for use cases such as invalidating all the cached data. To learn more, you can read [Mutate Multiple Keys](/docs/mutation#mutate-multiple-items) in the docs.
+
+```jsx
+import { mutate } from 'swr'
+// Or from the hook if you have customized your cache provider:
+// { mutate } = useSWRConfig()
+
+// Mutate single resource
+mutate(key)
+
+// Mutate multiple resources and clear the cache (set to undefined)
+mutate(
+ key => typeof key === 'string' && key.startsWith('/api/item?id='),
+ undefined,
+ { revalidate: false }
+)
+```
+
+## SWR DevTools
+
+[SWRDevTools](https://swr-devtools.vercel.app) is a browser extension that helps you debug your SWR cache and the fetch results. Check our [devtools](/docs/advanced/devtools) section for how to use devtools in your application.
+
+![](/img/devtools/cache-view.jpg)
+
+## Preloading Data
+
+Preloading data can improve the user experience tremendously. If you know the resource is going to be used later in the application, you can use the new `preload` API to start fetching it early:
+
+```jsx {6}
+import useSWR, { preload } from 'swr'
+
+const fetcher = (url) => fetch(url).then((res) => res.json())
+
+// You can call the preload function in anywhere
+preload('/api/user', fetcher)
+
+function Profile() {
+ // The component that actually uses the data:
+ const { data, error } = useSWR('/api/user', fetcher)
+ // ...
+}
+
+export function Page () {
+ return
+}
+```
+
+In this example, the `preload` API is called in the global scope. This means that we start to preload the resource before React even starts to render anything.
+And when the `Profile` component is being rendered, the data can probably be available already. If it’s still ongoing, the `useSWR` hook will reuse that ongoing preloading request instead of starting a new one.
+
+The `preload` API can also be used in cases like preloading data for another page that will likely be rendered. More information about prefetching data with SWR can be found [here](/docs/prefetching).
+
+## `isLoading`
+
+`isLoading` is a new state returned by `useSWR`, that indicates **if the request is still ongoing, and there is no data loaded yet**. Previously, the `isValidating` state represents both the initial loading state and revalidating state so we had to check if both `data` and `error` are `undefined` to determine if it was the initial loading state.
+
+Now, it is so easy that you can directly use the `isLoading` value to render a loading message:
+
+```jsx
+import useSWR from 'swr'
+
+function Profile() {
+ const { data, isLoading } = useSWR('/api/user', fetcher)
+
+ if (isLoading) return loading...
+ return hello {data.name}!
+}
+```
+
+Note that `isValidating` is still present so you can still use it to show a loading indicator for revalidations.
+
+
+ We have added the new [Understanding SWR](/docs/advanced/understanding) page to describe how SWR returns values, which includes the difference between `isValidating` and `isLoading`, and how to combine them to improve user experience.
+
+
+## Preserving Previous State
+
+The `keepPreviousData` option is a new addition that allows you to keep the data that was fetched before. This improves UX immensely when you’re fetching data based on user actions happening in real time, like with a live search feature, where the resource’s `key` keeps changing:
+
+```jsx {5}
+function Search() {
+ const [search, setSearch] = React.useState('');
+
+ const { data, isLoading } = useSWR(`/search?q=${search}`, fetcher, {
+ keepPreviousData: true
+ })
+
+ return (
+
+
setSearch(e.target.value)}
+ placeholder="Search..."
+ />
+
+
+ {data?.products.map(item =>
)
+
+
+ );
+}
+```
+
+
+
+Check the code on [CodeSandbox](https://codesandbox.io/s/swr-keeppreviousdata-fsjz3m) and you can read more about it [here](/docs/advanced/understanding#return-previous-data-for-better-ux).
+
+## Extending Configurations
+
+`SWRConfig` can now accept a function value. When you have multiple levels of ``, the inner receives the parent configuration and returns a new one. This change makes it more flexible to configure SWR in a large codebase. More information can be found [here](/docs/global-configuration).
+
+```jsx
+ ({
+ dedupingInterval: parentConfig.dedupingInterval * 5,
+ refreshInterval: 100,
+ })}
+>
+
+
+```
+
+## Improved React 18 Support
+
+SWR has updated its internal code to use `useSyncExternalStore` and `startTransition` APIs in React 18. These ensure stronger consistency when rendering UI concurrently. This change doesn’t require any user code changes and all developers will benefit from it directly. Shims are included for React 17 and below.
+
+SWR 2.0 and all the new features are still compatible with React 16 and 17.
+
+## Migration Guide
+
+### Fetcher No Longer Accepts Multiple Arguments
+
+`key` is now passed as a single argument.
+
+```diff
+- useSWR([1, 2, 3], (a, b, c) => {
++ useSWR([1, 2, 3], ([a, b, c]) => {
+ assert(a === 1)
+ assert(b === 2)
+ assert(c === 3)
+})
+```
+
+### Global Mutate No Longer Accepts a `getKey` Function
+
+Now, if you pass a function to the global `mutate`, it will be used as a [filter](/blog/swr-v2#mutate-multiple-keys). Previously, you can pass a function that returns a key to the global `mutate`:
+
+```diff
+- mutate(() => '/api/item') // a function to return a key
++ mutate('/api/item') // to mutate the key, directly pass it
+```
+
+### New Required Property `keys()` for Cache Interface
+
+When you use your own cache implementation, the Cache interface now requires a `keys()` method that returns all keys in the cache object, similar to the JavaScript Map instances.
+
+```diff
+interface Cache {
+ get(key: string): Data | undefined
+ set(key: string, value: Data): void
+ delete(key: string): void
++ keys(): IterableIterator
+}
+```
+
+### Changed Cache Internal Structure
+
+The internal structure of the cache data will be an object that holds all the current states.
+
+```diff
+- assert(cache.get(key) === data)
++ assert(cache.get(key) === { data, error, isValidating })
+
+// getter
+- cache.get(key)
++ cache.get(key)?.data
+
+// setter
+- cache.set(key, data)
++ cache.set(key, { ...cache.get(key), data })
+```
+
+
+ You should not write to the cache directly, it might cause undefined behavior.
+
+
+### `SWRConfig.default` Is Renamed as `SWRConfig.defaultValue`
+
+`SWRConfig.defaultValue` is the property for accessing the default SWR config.
+
+```diff
+- SWRConfig.default
++ SWRConfig.defaultValue
+```
+
+### Type `InfiniteFetcher` Is Renamed as `SWRInfiniteFetcher`
+
+```diff
+- import type { InfiniteFetcher } from 'swr/infinite'
++ import type { SWRInfiniteFetcher } from 'swr/infinite'
+```
+
+### Avoid Suspense on Server
+
+If you want to use `suspense: true` with SWR on the server-side, including pre-rendering in Next.js, then you must provide initial data via [`fallbackData` or `fallback`](/docs/with-nextjs#pre-rendering-with-default-data). Today, this means that you can't use Suspense to fetch data on the server side. Your other two options are doing fully client-side data-fetching or getting your framework to fetch the data for you (like getStaticProps does in Next.js).
+
+### ES2018 as the Build Target
+
+If you want to support IE 11, you have to target ES5 in your framework or a bundler. This change has made a performance improvement on SSR, and keeps the bundle size small.
+
+## Changelog
+
+Read the full Changelog [on GitHub](https://github.com/vercel/swr/releases).
+
+## The Future & Thank You!
+
+With the new release of [Next.js 13](https://nextjs.org/blog/next-13), we see a lot of exciting new things as well as paradigm shifts in the React ecosystem: [React Server Components](https://beta.nextjs.org/docs/rendering/server-and-client-components), streaming SSR, [async components](https://beta.nextjs.org/docs/data-fetching/fetching#asyncawait-in-server-components), and the [`use` hook](https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md#usepromise). Many of them are related to data-fetching, and some of them have overlapping use cases with SWR.
+
+However, the goal of the SWR project remains the same. We want it to be a drop-in library that is lightweight, framework agnostic, and a little bit _opinionated_ (i.e. revalidate upon focus). Instead of trying to be a standard solution, we want to focus on innovations that make the UX better. In the meantime, we are also doing research on how to improve SWR with these new abilities of React.
+
+We want to thank every one of the [143](https://github.com/vercel/swr/graphs/contributors) contributors (+ [106](https://github.com/vercel/swr-site/graphs/contributors) docs contributors), as well as those who helps us out or gave feedback. A special thanks goes to [Toru Kobayashi](https://twitter.com/koba04) for all his work on DevTools and docs– we couldn’t have done it without you!
\ No newline at end of file
diff --git a/pages/blog/swr-v2.ru.mdx b/pages/blog/swr-v2.ru.mdx
new file mode 100644
index 00000000..8d53655d
--- /dev/null
+++ b/pages/blog/swr-v2.ru.mdx
@@ -0,0 +1,410 @@
+---
+image: https://assets.vercel.com/image/upload/v1670542323/swr/v2.png
+description: 'Announcing SWR 2.0: new mutation APIs and improvd optimistic UI capabilities, new DevTools, better support for concurrent rendering, and more.'
+date: December 9th, 2022
+---
+
+import Callout from 'nextra-theme-docs/callout'
+import Bleed from 'nextra-theme-docs/bleed'
+
+import Authors, { Author } from 'components/authors'
+import Video from 'components/video'
+
+# Announcing SWR 2.0
+
+
+
+
+
+
+
+
+Today, we are excited to announce the release of SWR 2.0! This new version comes with a lot of improvements and new features: new mutation APIs and improvements to optimistic UI scenarios, new DevTools, better support for concurrent rendering, etc. We would like to give a big shout-out to all the contributors and maintainers who helped make this release possible.
+
+## Mutation and Optimistic UI
+
+### useSWRMutation
+
+Mutation is an important part of the data-fetching process. They allow you to make changes to your data both locally and remotely. Our existing `mutate` API allows you to revalidate and mutate resources manually. In SWR 2.0, the new hook `useSWRMutation` makes it even simpler to remotely change data using a declarative API. You can set up a mutation using the hook, and then activate it later:
+
+```jsx {11,16}
+import useSWRMutation from 'swr/mutation'
+
+async function sendRequest(url, { arg }) {
+ return fetch(url, {
+ method: 'POST',
+ body: JSON.stringify(arg)
+ })
+}
+
+function App() {
+ const { trigger, isMutating } = useSWRMutation('/api/user', sendRequest)
+
+ return (
+ trigger({ username: 'johndoe' })}
+ >{
+ isMutating ? 'Creating...' : 'Create User'
+ }
+ )
+}
+```
+
+The example above defines a `sendRequest` mutation that affects the `'/api/user'` resource. Unlike `useSWR`, `useSWRMutation` will not immediately start the request upon rendering. Instead, it returns a `trigger` function that can later be called to manually start the mutation.
+
+The `sendRequest` function will be called when the button is clicked, with the extra argument `{ username: 'johndoe' }`. The value of `isMutating` will be set to `true` until the mutation has finished.
+
+Additionally, this new hook addresses other issues you may have with mutations:
+
+- Optimistically update the UI while data is being mutated
+- Automatically revert when mutation fails
+- Avoid any potential race conditions between `useSWR` and other mutations of the same resource
+- Populate the `useSWR` cache after mutation completes
+- ...
+
+You can find in-depth API references and examples by reading the [docs](/docs/mutation#useswrmutation) or scrolling through the next few sections.
+
+### Optimistic UI
+
+Optimistic UI is an excellent model for creating websites that feel fast and responsive; however, it can be difficult to implement correctly. SWR 2.0 has added some new powerful options to make it easier.
+
+Let’s say we have an API that adds a new todo to the todo list and sends it to the server:
+
+```jsx
+await addNewTodo('New Item')
+```
+
+In our UI, we use a `useSWR` hook to display the todo list, with an “Add New Item” button that triggers this request and asks SWR to re-fetch the data via `mutate()`:
+
+```jsx {7,8}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ await addNewTodo('New Item')
+ mutate()
+ }}>
+ Add New Item
+
+>
+```
+
+However, the `await addNewTodo(...)` request could be very slow. When it’s ongoing, users still see the old list even if we can already know what the new list will look like. With the new `optimisticData` option, we can show the new list optimistically, before the server responds:
+
+```jsx {8}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+SWR will immediately update the `data` with the `optimisticData` value, and then send the request to the server. Once the request finishes, SWR will revalidate the resource to ensure it’s the latest.
+
+Like many APIs, if the `addNewTodo(...)` request returns us the latest data from the server, we can directly show that result, too (instead of starting a new revalidation)! There’s the new `populateCache` option to tell SWR to update the local data with the mutate response:
+
+```jsx {9}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ populateCache: true,
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+At the same time, we don’t need another revalidation afterward as the response data is from the source of truth, we can disable it with the `revalidate` option:
+
+```jsx {10}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ populateCache: true,
+ revalidate: false,
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+Lastly, if `addNewTodo(...)` fails with an exception, we can revert the optimistic data (`[...data, 'New Item']`) we just set, by setting `rollbackOnError` to `true` (which is also the default option). When that happens, SWR will roll back `data` to the previous value.
+
+```jsx {11}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ populateCache: true,
+ revalidate: false,
+ rollbackOnError: true,
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+All these APIs are supported in the new `useSWRMutation` hook as well. To learn more about them, you can check out our [docs](/docs/mutation#optimistic-updates). And here is a demo showing that behavior:
+
+
+
+### Mutate Multiple Keys
+
+The global `mutate` API now accepts a filter function, where you can mutate or revalidate specific keys. This will be helpful for use cases such as invalidating all the cached data. To learn more, you can read [Mutate Multiple Keys](/docs/mutation#mutate-multiple-items) in the docs.
+
+```jsx
+import { mutate } from 'swr'
+// Or from the hook if you have customized your cache provider:
+// { mutate } = useSWRConfig()
+
+// Mutate single resource
+mutate(key)
+
+// Mutate multiple resources and clear the cache (set to undefined)
+mutate(
+ key => typeof key === 'string' && key.startsWith('/api/item?id='),
+ undefined,
+ { revalidate: false }
+)
+```
+
+## SWR DevTools
+
+[SWRDevTools](https://swr-devtools.vercel.app) is a browser extension that helps you debug your SWR cache and the fetch results. Check our [devtools](/docs/advanced/devtools) section for how to use devtools in your application.
+
+![](/img/devtools/cache-view.jpg)
+
+## Preloading Data
+
+Preloading data can improve the user experience tremendously. If you know the resource is going to be used later in the application, you can use the new `preload` API to start fetching it early:
+
+```jsx {6}
+import useSWR, { preload } from 'swr'
+
+const fetcher = (url) => fetch(url).then((res) => res.json())
+
+// You can call the preload function in anywhere
+preload('/api/user', fetcher)
+
+function Profile() {
+ // The component that actually uses the data:
+ const { data, error } = useSWR('/api/user', fetcher)
+ // ...
+}
+
+export function Page () {
+ return
+}
+```
+
+In this example, the `preload` API is called in the global scope. This means that we start to preload the resource before React even starts to render anything.
+And when the `Profile` component is being rendered, the data can probably be available already. If it’s still ongoing, the `useSWR` hook will reuse that ongoing preloading request instead of starting a new one.
+
+The `preload` API can also be used in cases like preloading data for another page that will likely be rendered. More information about prefetching data with SWR can be found [here](/docs/prefetching).
+
+## `isLoading`
+
+`isLoading` is a new state returned by `useSWR`, that indicates **if the request is still ongoing, and there is no data loaded yet**. Previously, the `isValidating` state represents both the initial loading state and revalidating state so we had to check if both `data` and `error` are `undefined` to determine if it was the initial loading state.
+
+Now, it is so easy that you can directly use the `isLoading` value to render a loading message:
+
+```jsx
+import useSWR from 'swr'
+
+function Profile() {
+ const { data, isLoading } = useSWR('/api/user', fetcher)
+
+ if (isLoading) return loading...
+ return hello {data.name}!
+}
+```
+
+Note that `isValidating` is still present so you can still use it to show a loading indicator for revalidations.
+
+
+ We have added the new [Understanding SWR](/docs/advanced/understanding) page to describe how SWR returns values, which includes the difference between `isValidating` and `isLoading`, and how to combine them to improve user experience.
+
+
+## Preserving Previous State
+
+The `keepPreviousData` option is a new addition that allows you to keep the data that was fetched before. This improves UX immensely when you’re fetching data based on user actions happening in real time, like with a live search feature, where the resource’s `key` keeps changing:
+
+```jsx {5}
+function Search() {
+ const [search, setSearch] = React.useState('');
+
+ const { data, isLoading } = useSWR(`/search?q=${search}`, fetcher, {
+ keepPreviousData: true
+ })
+
+ return (
+
+
setSearch(e.target.value)}
+ placeholder="Search..."
+ />
+
+
+ {data?.products.map(item =>
)
+
+
+ );
+}
+```
+
+
+
+Check the code on [CodeSandbox](https://codesandbox.io/s/swr-keeppreviousdata-fsjz3m) and you can read more about it [here](/docs/advanced/understanding#return-previous-data-for-better-ux).
+
+## Extending Configurations
+
+`SWRConfig` can now accept a function value. When you have multiple levels of ``, the inner receives the parent configuration and returns a new one. This change makes it more flexible to configure SWR in a large codebase. More information can be found [here](/docs/global-configuration).
+
+```jsx
+ ({
+ dedupingInterval: parentConfig.dedupingInterval * 5,
+ refreshInterval: 100,
+ })}
+>
+
+
+```
+
+## Improved React 18 Support
+
+SWR has updated its internal code to use `useSyncExternalStore` and `startTransition` APIs in React 18. These ensure stronger consistency when rendering UI concurrently. This change doesn’t require any user code changes and all developers will benefit from it directly. Shims are included for React 17 and below.
+
+SWR 2.0 and all the new features are still compatible with React 16 and 17.
+
+## Migration Guide
+
+### Fetcher No Longer Accepts Multiple Arguments
+
+`key` is now passed as a single argument.
+
+```diff
+- useSWR([1, 2, 3], (a, b, c) => {
++ useSWR([1, 2, 3], ([a, b, c]) => {
+ assert(a === 1)
+ assert(b === 2)
+ assert(c === 3)
+})
+```
+
+### Global Mutate No Longer Accepts a `getKey` Function
+
+Now, if you pass a function to the global `mutate`, it will be used as a [filter](/blog/swr-v2#mutate-multiple-keys). Previously, you can pass a function that returns a key to the global `mutate`:
+
+```diff
+- mutate(() => '/api/item') // a function to return a key
++ mutate('/api/item') // to mutate the key, directly pass it
+```
+
+### New Required Property `keys()` for Cache Interface
+
+When you use your own cache implementation, the Cache interface now requires a `keys()` method that returns all keys in the cache object, similar to the JavaScript Map instances.
+
+```diff
+interface Cache {
+ get(key: string): Data | undefined
+ set(key: string, value: Data): void
+ delete(key: string): void
++ keys(): IterableIterator
+}
+```
+
+### Changed Cache Internal Structure
+
+The internal structure of the cache data will be an object that holds all the current states.
+
+```diff
+- assert(cache.get(key) === data)
++ assert(cache.get(key) === { data, error, isValidating })
+
+// getter
+- cache.get(key)
++ cache.get(key)?.data
+
+// setter
+- cache.set(key, data)
++ cache.set(key, { ...cache.get(key), data })
+```
+
+
+ You should not write to the cache directly, it might cause undefined behavior.
+
+
+### `SWRConfig.default` Is Renamed as `SWRConfig.defaultValue`
+
+`SWRConfig.defaultValue` is the property for accessing the default SWR config.
+
+```diff
+- SWRConfig.default
++ SWRConfig.defaultValue
+```
+
+### Type `InfiniteFetcher` Is Renamed as `SWRInfiniteFetcher`
+
+```diff
+- import type { InfiniteFetcher } from 'swr/infinite'
++ import type { SWRInfiniteFetcher } from 'swr/infinite'
+```
+
+### Avoid Suspense on Server
+
+If you want to use `suspense: true` with SWR on the server-side, including pre-rendering in Next.js, then you must provide initial data via [`fallbackData` or `fallback`](/docs/with-nextjs#pre-rendering-with-default-data). Today, this means that you can't use Suspense to fetch data on the server side. Your other two options are doing fully client-side data-fetching or getting your framework to fetch the data for you (like getStaticProps does in Next.js).
+
+### ES2018 as the Build Target
+
+If you want to support IE 11, you have to target ES5 in your framework or a bundler. This change has made a performance improvement on SSR, and keeps the bundle size small.
+
+## Changelog
+
+Read the full Changelog [on GitHub](https://github.com/vercel/swr/releases).
+
+## The Future & Thank You!
+
+With the new release of [Next.js 13](https://nextjs.org/blog/next-13), we see a lot of exciting new things as well as paradigm shifts in the React ecosystem: [React Server Components](https://beta.nextjs.org/docs/rendering/server-and-client-components), streaming SSR, [async components](https://beta.nextjs.org/docs/data-fetching/fetching#asyncawait-in-server-components), and the [`use` hook](https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md#usepromise). Many of them are related to data-fetching, and some of them have overlapping use cases with SWR.
+
+However, the goal of the SWR project remains the same. We want it to be a drop-in library that is lightweight, framework agnostic, and a little bit _opinionated_ (i.e. revalidate upon focus). Instead of trying to be a standard solution, we want to focus on innovations that make the UX better. In the meantime, we are also doing research on how to improve SWR with these new abilities of React.
+
+We want to thank every one of the [143](https://github.com/vercel/swr/graphs/contributors) contributors (+ [106](https://github.com/vercel/swr-site/graphs/contributors) docs contributors), as well as those who helps us out or gave feedback. A special thanks goes to [Toru Kobayashi](https://twitter.com/koba04) for all his work on DevTools and docs– we couldn’t have done it without you!
\ No newline at end of file
diff --git a/pages/blog/swr-v2.zh-CN.mdx b/pages/blog/swr-v2.zh-CN.mdx
new file mode 100644
index 00000000..8d53655d
--- /dev/null
+++ b/pages/blog/swr-v2.zh-CN.mdx
@@ -0,0 +1,410 @@
+---
+image: https://assets.vercel.com/image/upload/v1670542323/swr/v2.png
+description: 'Announcing SWR 2.0: new mutation APIs and improvd optimistic UI capabilities, new DevTools, better support for concurrent rendering, and more.'
+date: December 9th, 2022
+---
+
+import Callout from 'nextra-theme-docs/callout'
+import Bleed from 'nextra-theme-docs/bleed'
+
+import Authors, { Author } from 'components/authors'
+import Video from 'components/video'
+
+# Announcing SWR 2.0
+
+
+
+
+
+
+
+
+Today, we are excited to announce the release of SWR 2.0! This new version comes with a lot of improvements and new features: new mutation APIs and improvements to optimistic UI scenarios, new DevTools, better support for concurrent rendering, etc. We would like to give a big shout-out to all the contributors and maintainers who helped make this release possible.
+
+## Mutation and Optimistic UI
+
+### useSWRMutation
+
+Mutation is an important part of the data-fetching process. They allow you to make changes to your data both locally and remotely. Our existing `mutate` API allows you to revalidate and mutate resources manually. In SWR 2.0, the new hook `useSWRMutation` makes it even simpler to remotely change data using a declarative API. You can set up a mutation using the hook, and then activate it later:
+
+```jsx {11,16}
+import useSWRMutation from 'swr/mutation'
+
+async function sendRequest(url, { arg }) {
+ return fetch(url, {
+ method: 'POST',
+ body: JSON.stringify(arg)
+ })
+}
+
+function App() {
+ const { trigger, isMutating } = useSWRMutation('/api/user', sendRequest)
+
+ return (
+ trigger({ username: 'johndoe' })}
+ >{
+ isMutating ? 'Creating...' : 'Create User'
+ }
+ )
+}
+```
+
+The example above defines a `sendRequest` mutation that affects the `'/api/user'` resource. Unlike `useSWR`, `useSWRMutation` will not immediately start the request upon rendering. Instead, it returns a `trigger` function that can later be called to manually start the mutation.
+
+The `sendRequest` function will be called when the button is clicked, with the extra argument `{ username: 'johndoe' }`. The value of `isMutating` will be set to `true` until the mutation has finished.
+
+Additionally, this new hook addresses other issues you may have with mutations:
+
+- Optimistically update the UI while data is being mutated
+- Automatically revert when mutation fails
+- Avoid any potential race conditions between `useSWR` and other mutations of the same resource
+- Populate the `useSWR` cache after mutation completes
+- ...
+
+You can find in-depth API references and examples by reading the [docs](/docs/mutation#useswrmutation) or scrolling through the next few sections.
+
+### Optimistic UI
+
+Optimistic UI is an excellent model for creating websites that feel fast and responsive; however, it can be difficult to implement correctly. SWR 2.0 has added some new powerful options to make it easier.
+
+Let’s say we have an API that adds a new todo to the todo list and sends it to the server:
+
+```jsx
+await addNewTodo('New Item')
+```
+
+In our UI, we use a `useSWR` hook to display the todo list, with an “Add New Item” button that triggers this request and asks SWR to re-fetch the data via `mutate()`:
+
+```jsx {7,8}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ await addNewTodo('New Item')
+ mutate()
+ }}>
+ Add New Item
+
+>
+```
+
+However, the `await addNewTodo(...)` request could be very slow. When it’s ongoing, users still see the old list even if we can already know what the new list will look like. With the new `optimisticData` option, we can show the new list optimistically, before the server responds:
+
+```jsx {8}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+SWR will immediately update the `data` with the `optimisticData` value, and then send the request to the server. Once the request finishes, SWR will revalidate the resource to ensure it’s the latest.
+
+Like many APIs, if the `addNewTodo(...)` request returns us the latest data from the server, we can directly show that result, too (instead of starting a new revalidation)! There’s the new `populateCache` option to tell SWR to update the local data with the mutate response:
+
+```jsx {9}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ populateCache: true,
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+At the same time, we don’t need another revalidation afterward as the response data is from the source of truth, we can disable it with the `revalidate` option:
+
+```jsx {10}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ populateCache: true,
+ revalidate: false,
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+Lastly, if `addNewTodo(...)` fails with an exception, we can revert the optimistic data (`[...data, 'New Item']`) we just set, by setting `rollbackOnError` to `true` (which is also the default option). When that happens, SWR will roll back `data` to the previous value.
+
+```jsx {11}
+const { mutate, data } = useSWR('/api/todos')
+
+return <>
+
+
+ {
+ mutate(addNewTodo('New Item'), {
+ optimisticData: [...data, 'New Item'],
+ populateCache: true,
+ revalidate: false,
+ rollbackOnError: true,
+ })
+ }}>
+ Add New Item
+
+>
+```
+
+All these APIs are supported in the new `useSWRMutation` hook as well. To learn more about them, you can check out our [docs](/docs/mutation#optimistic-updates). And here is a demo showing that behavior:
+
+
+
+### Mutate Multiple Keys
+
+The global `mutate` API now accepts a filter function, where you can mutate or revalidate specific keys. This will be helpful for use cases such as invalidating all the cached data. To learn more, you can read [Mutate Multiple Keys](/docs/mutation#mutate-multiple-items) in the docs.
+
+```jsx
+import { mutate } from 'swr'
+// Or from the hook if you have customized your cache provider:
+// { mutate } = useSWRConfig()
+
+// Mutate single resource
+mutate(key)
+
+// Mutate multiple resources and clear the cache (set to undefined)
+mutate(
+ key => typeof key === 'string' && key.startsWith('/api/item?id='),
+ undefined,
+ { revalidate: false }
+)
+```
+
+## SWR DevTools
+
+[SWRDevTools](https://swr-devtools.vercel.app) is a browser extension that helps you debug your SWR cache and the fetch results. Check our [devtools](/docs/advanced/devtools) section for how to use devtools in your application.
+
+![](/img/devtools/cache-view.jpg)
+
+## Preloading Data
+
+Preloading data can improve the user experience tremendously. If you know the resource is going to be used later in the application, you can use the new `preload` API to start fetching it early:
+
+```jsx {6}
+import useSWR, { preload } from 'swr'
+
+const fetcher = (url) => fetch(url).then((res) => res.json())
+
+// You can call the preload function in anywhere
+preload('/api/user', fetcher)
+
+function Profile() {
+ // The component that actually uses the data:
+ const { data, error } = useSWR('/api/user', fetcher)
+ // ...
+}
+
+export function Page () {
+ return
+}
+```
+
+In this example, the `preload` API is called in the global scope. This means that we start to preload the resource before React even starts to render anything.
+And when the `Profile` component is being rendered, the data can probably be available already. If it’s still ongoing, the `useSWR` hook will reuse that ongoing preloading request instead of starting a new one.
+
+The `preload` API can also be used in cases like preloading data for another page that will likely be rendered. More information about prefetching data with SWR can be found [here](/docs/prefetching).
+
+## `isLoading`
+
+`isLoading` is a new state returned by `useSWR`, that indicates **if the request is still ongoing, and there is no data loaded yet**. Previously, the `isValidating` state represents both the initial loading state and revalidating state so we had to check if both `data` and `error` are `undefined` to determine if it was the initial loading state.
+
+Now, it is so easy that you can directly use the `isLoading` value to render a loading message:
+
+```jsx
+import useSWR from 'swr'
+
+function Profile() {
+ const { data, isLoading } = useSWR('/api/user', fetcher)
+
+ if (isLoading) return loading...
+ return hello {data.name}!
+}
+```
+
+Note that `isValidating` is still present so you can still use it to show a loading indicator for revalidations.
+
+
+ We have added the new [Understanding SWR](/docs/advanced/understanding) page to describe how SWR returns values, which includes the difference between `isValidating` and `isLoading`, and how to combine them to improve user experience.
+
+
+## Preserving Previous State
+
+The `keepPreviousData` option is a new addition that allows you to keep the data that was fetched before. This improves UX immensely when you’re fetching data based on user actions happening in real time, like with a live search feature, where the resource’s `key` keeps changing:
+
+```jsx {5}
+function Search() {
+ const [search, setSearch] = React.useState('');
+
+ const { data, isLoading } = useSWR(`/search?q=${search}`, fetcher, {
+ keepPreviousData: true
+ })
+
+ return (
+
+
setSearch(e.target.value)}
+ placeholder="Search..."
+ />
+
+
+ {data?.products.map(item =>
)
+
+
+ );
+}
+```
+
+
+
+Check the code on [CodeSandbox](https://codesandbox.io/s/swr-keeppreviousdata-fsjz3m) and you can read more about it [here](/docs/advanced/understanding#return-previous-data-for-better-ux).
+
+## Extending Configurations
+
+`SWRConfig` can now accept a function value. When you have multiple levels of ``, the inner receives the parent configuration and returns a new one. This change makes it more flexible to configure SWR in a large codebase. More information can be found [here](/docs/global-configuration).
+
+```jsx
+ ({
+ dedupingInterval: parentConfig.dedupingInterval * 5,
+ refreshInterval: 100,
+ })}
+>
+
+
+```
+
+## Improved React 18 Support
+
+SWR has updated its internal code to use `useSyncExternalStore` and `startTransition` APIs in React 18. These ensure stronger consistency when rendering UI concurrently. This change doesn’t require any user code changes and all developers will benefit from it directly. Shims are included for React 17 and below.
+
+SWR 2.0 and all the new features are still compatible with React 16 and 17.
+
+## Migration Guide
+
+### Fetcher No Longer Accepts Multiple Arguments
+
+`key` is now passed as a single argument.
+
+```diff
+- useSWR([1, 2, 3], (a, b, c) => {
++ useSWR([1, 2, 3], ([a, b, c]) => {
+ assert(a === 1)
+ assert(b === 2)
+ assert(c === 3)
+})
+```
+
+### Global Mutate No Longer Accepts a `getKey` Function
+
+Now, if you pass a function to the global `mutate`, it will be used as a [filter](/blog/swr-v2#mutate-multiple-keys). Previously, you can pass a function that returns a key to the global `mutate`:
+
+```diff
+- mutate(() => '/api/item') // a function to return a key
++ mutate('/api/item') // to mutate the key, directly pass it
+```
+
+### New Required Property `keys()` for Cache Interface
+
+When you use your own cache implementation, the Cache interface now requires a `keys()` method that returns all keys in the cache object, similar to the JavaScript Map instances.
+
+```diff
+interface Cache {
+ get(key: string): Data | undefined
+ set(key: string, value: Data): void
+ delete(key: string): void
++ keys(): IterableIterator
+}
+```
+
+### Changed Cache Internal Structure
+
+The internal structure of the cache data will be an object that holds all the current states.
+
+```diff
+- assert(cache.get(key) === data)
++ assert(cache.get(key) === { data, error, isValidating })
+
+// getter
+- cache.get(key)
++ cache.get(key)?.data
+
+// setter
+- cache.set(key, data)
++ cache.set(key, { ...cache.get(key), data })
+```
+
+
+ You should not write to the cache directly, it might cause undefined behavior.
+
+
+### `SWRConfig.default` Is Renamed as `SWRConfig.defaultValue`
+
+`SWRConfig.defaultValue` is the property for accessing the default SWR config.
+
+```diff
+- SWRConfig.default
++ SWRConfig.defaultValue
+```
+
+### Type `InfiniteFetcher` Is Renamed as `SWRInfiniteFetcher`
+
+```diff
+- import type { InfiniteFetcher } from 'swr/infinite'
++ import type { SWRInfiniteFetcher } from 'swr/infinite'
+```
+
+### Avoid Suspense on Server
+
+If you want to use `suspense: true` with SWR on the server-side, including pre-rendering in Next.js, then you must provide initial data via [`fallbackData` or `fallback`](/docs/with-nextjs#pre-rendering-with-default-data). Today, this means that you can't use Suspense to fetch data on the server side. Your other two options are doing fully client-side data-fetching or getting your framework to fetch the data for you (like getStaticProps does in Next.js).
+
+### ES2018 as the Build Target
+
+If you want to support IE 11, you have to target ES5 in your framework or a bundler. This change has made a performance improvement on SSR, and keeps the bundle size small.
+
+## Changelog
+
+Read the full Changelog [on GitHub](https://github.com/vercel/swr/releases).
+
+## The Future & Thank You!
+
+With the new release of [Next.js 13](https://nextjs.org/blog/next-13), we see a lot of exciting new things as well as paradigm shifts in the React ecosystem: [React Server Components](https://beta.nextjs.org/docs/rendering/server-and-client-components), streaming SSR, [async components](https://beta.nextjs.org/docs/data-fetching/fetching#asyncawait-in-server-components), and the [`use` hook](https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md#usepromise). Many of them are related to data-fetching, and some of them have overlapping use cases with SWR.
+
+However, the goal of the SWR project remains the same. We want it to be a drop-in library that is lightweight, framework agnostic, and a little bit _opinionated_ (i.e. revalidate upon focus). Instead of trying to be a standard solution, we want to focus on innovations that make the UX better. In the meantime, we are also doing research on how to improve SWR with these new abilities of React.
+
+We want to thank every one of the [143](https://github.com/vercel/swr/graphs/contributors) contributors (+ [106](https://github.com/vercel/swr-site/graphs/contributors) docs contributors), as well as those who helps us out or gave feedback. A special thanks goes to [Toru Kobayashi](https://twitter.com/koba04) for all his work on DevTools and docs– we couldn’t have done it without you!
\ No newline at end of file
diff --git a/pages/docs/advanced/cache.en-US.mdx b/pages/docs/advanced/cache.en-US.mdx
index 524840dd..eb856181 100644
--- a/pages/docs/advanced/cache.en-US.mdx
+++ b/pages/docs/advanced/cache.en-US.mdx
@@ -24,6 +24,7 @@ interface Cache {
get(key: string): Data | undefined
set(key: string, value: Data): void
delete(key: string): void
+ keys(): IterableIterator
}
```
@@ -60,7 +61,7 @@ import { Cache } from 'components/diagrams/cache'
When nested, SWR hooks will use the upper-level cache provider. If there is no upper-level cache provider, it fallbacks to the default cache provider, which is an empty `Map`.
- If a cache provider is used, the global `mutate` will **not** work for SWR hooks under that `` boundary. Please use [this](#access-current-cache-provider) instead.
+ If a cache provider is used, the global `mutate` will **not** work for SWR hooks under that `` boundary. Please use [this](#access-current-cache-provider) instead.
## Access Current Cache Provider
@@ -187,13 +188,22 @@ describe('test suite', async () => {
})
```
-### Access to the Cache
+### Modify the Cache Data
-Alert: you should not write to the cache directly, it might cause undefined behavior.
+
+ You should not write to the cache directly, it might cause undefined behavior.
+
+
+You can use [`mutate`](/docs/mutation) to modify the cache. For example, you can clear all cache data like the following.
```jsx
const { cache } = useSWRConfig()
-cache.get(key) // Get the current data for a key.
-cache.clear() // ⚠️ Clear all the cache. SWR will revalidate upon re-render.
+mutate(
+ key => true, // which cache keys are updated
+ undefined, // update cache data to `undefined`
+ { revalidate: false } // do not revalidate
+)
```
+
+More information can be found [here](/docs/arguments#multiple-arguments).
diff --git a/pages/docs/advanced/cache.ja.mdx b/pages/docs/advanced/cache.ja.mdx
index c03ab01f..568604f5 100644
--- a/pages/docs/advanced/cache.ja.mdx
+++ b/pages/docs/advanced/cache.ja.mdx
@@ -24,6 +24,7 @@ interface Cache {
get(key: string): Data | undefined
set(key: string, value: Data): void
delete(key: string): void
+ keys(): IterableIterator
}
```
@@ -187,13 +188,28 @@ describe('test suite', async () => {
})
```
-### キャッシュへアクセスする
-
-警告:キャッシュに直接書き込むことはするべきではありません。予期しない動作が発生する可能性があります。
+### キャッシュを更新する
```jsx
const { cache } = useSWRConfig()
cache.get(key) // キーの現在のデータを取得します。
-cache.clear() // ⚠️ すべてのキャッシュをクリアします。 SWRは、再レンダリング時に再検証します。
```
+
+
+ キャッシュに直接書き込むことはするべきではありません。予期しない動作が発生する可能性があります。
+
+
+[`mutate`](/docs/mutation) をキャッシュを更新するために利用できます。例えば、下記のようにすることで全てのキャッシュデータをクリアできます。
+
+```jsx
+const { cache } = useSWRConfig()
+
+mutate(
+ key => true, // どのキャッシュキーを更新するか
+ undefined, // キャッシュデータを `undefined` に更新する
+ { revalidate: false } // 再検証しない
+)
+```
+
+更なる情報は [こちら](/docs/arguments#multiple-arguments) から確認できます。
\ No newline at end of file
diff --git a/pages/docs/advanced/cache.ko.mdx b/pages/docs/advanced/cache.ko.mdx
index 2c6b013f..7b85112c 100644
--- a/pages/docs/advanced/cache.ko.mdx
+++ b/pages/docs/advanced/cache.ko.mdx
@@ -24,6 +24,7 @@ interface Cache {
get(key: string): Data | undefined
set(key: string, value: Data): void
delete(key: string): void
+ keys(): IterableIterator
}
```
@@ -187,13 +188,28 @@ describe('test suite', async () => {
})
```
-### 캐시에 접근하기
-
-알림: 캐시를 직접 쓰면 안 됩니다. 정의되지 않은 동작을 유발할 수도 있습니다.
+### Modify the Cache Data
```jsx
const { cache } = useSWRConfig()
cache.get(key) // 키에 대한 현재 데이터 가져오기.
-cache.clear() // ⚠️ 모든 캐시를 삭제하기. SWR은 다시 렌더링할 때 재검증합니다.
```
+
+
+ 캐시를 직접 쓰면 안 됩니다. 정의되지 않은 동작을 유발할 수도 있습니다.
+
+
+You can use [`mutate`](/docs/mutation) to modify the cache. For example, you can clear all cache data like the following.
+
+```jsx
+const { cache } = useSWRConfig()
+
+mutate(
+ key => true, // which cache keys are updated
+ undefined, // update cache data to `undefined`
+ { revalidate: false } // do not revalidate
+)
+```
+
+More information can be found [here](/docs/arguments#multiple-arguments).
diff --git a/pages/docs/advanced/cache.pt-BR.mdx b/pages/docs/advanced/cache.pt-BR.mdx
index 19c83174..8b35d711 100644
--- a/pages/docs/advanced/cache.pt-BR.mdx
+++ b/pages/docs/advanced/cache.pt-BR.mdx
@@ -1,202 +1,218 @@
-import Callout from 'nextra-theme-docs/callout'
-
-# Cache
-
-
- Atualize para a última versão (≥ 1.0.0) para usar este recurso.
-
-
-
-
-Na maioria dos casos, você não deve escrever diretamente no cache, o que pode causar comportamentos indefinidos do SWR.
-Se você precisa mutar manualmente uma chave, por favor, considere usar as APIs do SWR.
-Veja também: [Mutação](/docs/mutation), [Redefinir Cache Entre Casos de Teste](#redefinir-cache-entre-casos-de-teste).
-
-
-
-Por padrão, SWR usa um cache global para armazenar e compartilhar dados entre todos os componentes. Mas você pode customizar este comportamento com a opção `provider` do `SWRConfig`.
-
-Cache Providers são destinados a permitir SWR com armazenamentos mais personalizados.
-
-## Cache Provider
-
-Um cache provider é um objeto (parecido com um Map) que corresponde à definição de tipo TypeScript da seguinte definição (que pode ser importado de `swr`):
-
-```typescript
-interface Cache {
- get(key: string): Data | undefined
- set(key: string, value: Data): void
- delete(key: string): void
-}
-```
-
-Por exemplo, um [Map do JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) pode ser usado diretamente como cache provider para SWR.
-
-## Criar Cache Provider
-
-A opção `provider` do `SWRConfig` recebe uma função que retorna um [cache provider](#cache-provider). O provider será usado por todos os hooks SWR dentro dos limites de configuração do `SWRConfig`. Por exemplo:
-
-```jsx
-import useSWR, { SWRConfig } from 'swr'
-
-function App() {
- return (
- new Map() }}>
-
-
- )
-}
-```
-
-Todos os hooks SWR dentro de ` ` lerão e escreverão naquela instância Map. Você também pode usar outras implementações de cache provider para seu caso específico.
-
-
- No exemplo acima, quando o componente ` ` é re-montado, o provider também é re-criado. Cache Providers devem ser colocados mais alto na árvore de componentes, ou fora do render.
-
-
-import { Cache } from 'components/diagrams/cache'
-
-
-
-
-
-Quando aninhado, hooks SWR irão usar o cache provider mais alto. Se não houver cache provider mais alto, ele irá usar o provider padrão, que é um `Map` vazio.
-
-
- Se um cache provider é usado, o `mutate` global **não** funcionará para os hooks SWR dentro do limite de configuração ``. Por favor, use [isto](#acessar-cache-provider-atual) em vez disso.
-
-
-## Acessar Cache Provider Atual
-
-Quando dentro de um Componente React, você precisa usar o hook [`useSWRConfig`](/docs/global-configuration#acesso-às-configurações-globais) para obter acesso ao cache provider atual e outras configurações, incluindo `mutate`:
-
-```jsx
-import { useSWRConfig } from 'swr'
-
-function Avatar() {
- const { cache, mutate, ...extraConfig } = useSWRConfig()
- // ...
-}
-```
-
-Se não estiver sob nenhum ``, será retornado as configurações padrão.
-
-## Experimental: Estender o Cache Provider
-
-
- Este é um recurso experimental, o comportamento pode mudar em futuras atualizações.
-
-
-Quando vários componentes `` são aninhados, o cache provider pode ser estendido.
-
-O primeiro argumento para a função `provider` é o cache provider do nível superior do `` (ou o cache padrão se não houver `` pai), você pode usá-lo para estender o cache provider:
-
-```jsx
- newCache }}>
- ...
-
-```
-
-## Exemplos
-
-### Mudar Várias Chaves de RegEx
-
-Com a flexibilidade da API do cache provider, você pode até criar um auxiliar de "mutação parcial".
-
-No exemplo abaixo, `matchMutate` pode receber uma expressão regular como chave e ser usada para alterar aqueles que corresponderem a esse padrão.
-
-```js
-function useMatchMutate() {
- const { cache, mutate } = useSWRConfig()
- return (matcher, ...args) => {
- if (!(cache instanceof Map)) {
- throw new Error('matchMutate requer que o cache provider seja uma instância de Map')
- }
-
- const keys = []
-
- for (const key of cache.keys()) {
- if (matcher.test(key)) {
- keys.push(key)
- }
- }
-
- const mutations = keys.map((key) => mutate(key, ...args))
- return Promise.all(mutations)
- }
-}
-```
-
-Em seguida, dentro de seu componente:
-
-```jsx
-function Button() {
- const matchMutate = useMatchMutate()
- return matchMutate(/^\/api\//)}>
- Revalidar todas as chaves começando com "/api/"
-
-}
-```
-
-
- Observe que este exemplo requer que o cache provider seja uma instância de Map.
-
-
-### Cache Persistente Baseado em LocalStorage
-
-Você pode querer sincronizar seu cache com `localStorage`. Aqui está um exemplo de implementação:
-
-```jsx
-function localStorageProvider() {
- // Ao inicializar, restauramos os dados de `localStorage` em um mapa.
- const map = new Map(JSON.parse(localStorage.getItem('app-cache') || '[]'))
-
- // Antes de descarregar o aplicativo, gravamos todos os dados em `localStorage`.
- window.addEventListener('beforeunload', () => {
- const appCache = JSON.stringify(Array.from(map.entries()))
- localStorage.setItem('app-cache', appCache)
- })
-
- // Ainda usamos o mapa para gravação e leitura para desempenho.
- return map
-}
-```
-
-Em seguida, use-o como provider:
-
-```jsx
-
-
-
-```
-
-
- Como melhoria, você também pode usar o cache de memória como um buffer e gravar em `localStorage` periodicamente. Você também pode implementar um cache em camadas semelhante ao IndexedDB ou WebSQL.
-
-
-### Redefinir Cache Entre Casos de Teste
-
-Ao testar seu aplicativo, talvez você queira redefinir o cache SWR entre os casos de teste. Você pode simplesmente envolver seu aplicativo com um cache provider vazio. Aqui está um exemplo com Jest:
-
-```jsx
-describe('test suite', async () => {
- it('test case', async () => {
- render(
- new Map() }}>
-
-
- )
- })
-})
-```
-
-### Acesso ao Cache
-
-Alerta: você não deve gravar diretamente no cache, isso pode causar um comportamento indefinido.
-
-```jsx
-const { cache } = useSWRConfig()
-
-cache.get(key) // Obtém os dados atuais para uma chave.
-cache.clear() // ⚠️ Limpa todo o cache. O SWR será revalidado após a re-renderização.
-```
+import Callout from 'nextra-theme-docs/callout'
+
+# Cache
+
+
+ Atualize para a última versão (≥ 1.0.0) para usar este recurso.
+
+
+
+
+Na maioria dos casos, você não deve escrever diretamente no cache, o que pode causar comportamentos indefinidos do SWR.
+Se você precisa mutar manualmente uma chave, por favor, considere usar as APIs do SWR.
+Veja também: [Mutação](/docs/mutation), [Redefinir Cache Entre Casos de Teste](#redefinir-cache-entre-casos-de-teste).
+
+
+
+Por padrão, SWR usa um cache global para armazenar e compartilhar dados entre todos os componentes. Mas você pode customizar este comportamento com a opção `provider` do `SWRConfig`.
+
+Cache Providers são destinados a permitir SWR com armazenamentos mais personalizados.
+
+## Cache Provider
+
+Um cache provider é um objeto (parecido com um Map) que corresponde à definição de tipo TypeScript da seguinte definição (que pode ser importado de `swr`):
+
+```typescript
+interface Cache {
+ get(key: string): Data | undefined
+ set(key: string, value: Data): void
+ delete(key: string): void
+ keys(): IterableIterator
+}
+```
+
+Por exemplo, um [Map do JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) pode ser usado diretamente como cache provider para SWR.
+
+## Criar Cache Provider
+
+A opção `provider` do `SWRConfig` recebe uma função que retorna um [cache provider](#cache-provider). O provider será usado por todos os hooks SWR dentro dos limites de configuração do `SWRConfig`. Por exemplo:
+
+```jsx
+import useSWR, { SWRConfig } from 'swr'
+
+function App() {
+ return (
+ new Map() }}>
+
+
+ )
+}
+```
+
+Todos os hooks SWR dentro de ` ` lerão e escreverão naquela instância Map. Você também pode usar outras implementações de cache provider para seu caso específico.
+
+
+ No exemplo acima, quando o componente ` ` é re-montado, o provider também é re-criado. Cache Providers devem ser colocados mais alto na árvore de componentes, ou fora do render.
+
+
+import { Cache } from 'components/diagrams/cache'
+
+
+
+
+
+Quando aninhado, hooks SWR irão usar o cache provider mais alto. Se não houver cache provider mais alto, ele irá usar o provider padrão, que é um `Map` vazio.
+
+
+ Se um cache provider é usado, o `mutate` global **não** funcionará para os hooks SWR dentro do limite de configuração ``. Por favor, use [isto](#acessar-cache-provider-atual) em vez disso.
+
+
+## Acessar Cache Provider Atual
+
+Quando dentro de um Componente React, você precisa usar o hook [`useSWRConfig`](/docs/global-configuration#acesso-às-configurações-globais) para obter acesso ao cache provider atual e outras configurações, incluindo `mutate`:
+
+```jsx
+import { useSWRConfig } from 'swr'
+
+function Avatar() {
+ const { cache, mutate, ...extraConfig } = useSWRConfig()
+ // ...
+}
+```
+
+Se não estiver sob nenhum ``, será retornado as configurações padrão.
+
+## Experimental: Estender o Cache Provider
+
+
+ Este é um recurso experimental, o comportamento pode mudar em futuras atualizações.
+
+
+Quando vários componentes `` são aninhados, o cache provider pode ser estendido.
+
+O primeiro argumento para a função `provider` é o cache provider do nível superior do `` (ou o cache padrão se não houver `` pai), você pode usá-lo para estender o cache provider:
+
+```jsx
+ newCache }}>
+ ...
+
+```
+
+## Exemplos
+
+### Mudar Várias Chaves de RegEx
+
+Com a flexibilidade da API do cache provider, você pode até criar um auxiliar de "mutação parcial".
+
+No exemplo abaixo, `matchMutate` pode receber uma expressão regular como chave e ser usada para alterar aqueles que corresponderem a esse padrão.
+
+```js
+function useMatchMutate() {
+ const { cache, mutate } = useSWRConfig()
+ return (matcher, ...args) => {
+ if (!(cache instanceof Map)) {
+ throw new Error('matchMutate requer que o cache provider seja uma instância de Map')
+ }
+
+ const keys = []
+
+ for (const key of cache.keys()) {
+ if (matcher.test(key)) {
+ keys.push(key)
+ }
+ }
+
+ const mutations = keys.map((key) => mutate(key, ...args))
+ return Promise.all(mutations)
+ }
+}
+```
+
+Em seguida, dentro de seu componente:
+
+```jsx
+function Button() {
+ const matchMutate = useMatchMutate()
+ return matchMutate(/^\/api\//)}>
+ Revalidar todas as chaves começando com "/api/"
+
+}
+```
+
+
+ Observe que este exemplo requer que o cache provider seja uma instância de Map.
+
+
+### Cache Persistente Baseado em LocalStorage
+
+Você pode querer sincronizar seu cache com `localStorage`. Aqui está um exemplo de implementação:
+
+```jsx
+function localStorageProvider() {
+ // Ao inicializar, restauramos os dados de `localStorage` em um mapa.
+ const map = new Map(JSON.parse(localStorage.getItem('app-cache') || '[]'))
+
+ // Antes de descarregar o aplicativo, gravamos todos os dados em `localStorage`.
+ window.addEventListener('beforeunload', () => {
+ const appCache = JSON.stringify(Array.from(map.entries()))
+ localStorage.setItem('app-cache', appCache)
+ })
+
+ // Ainda usamos o mapa para gravação e leitura para desempenho.
+ return map
+}
+```
+
+Em seguida, use-o como provider:
+
+```jsx
+
+
+
+```
+
+
+ Como melhoria, você também pode usar o cache de memória como um buffer e gravar em `localStorage` periodicamente. Você também pode implementar um cache em camadas semelhante ao IndexedDB ou WebSQL.
+
+
+### Redefinir Cache Entre Casos de Teste
+
+Ao testar seu aplicativo, talvez você queira redefinir o cache SWR entre os casos de teste. Você pode simplesmente envolver seu aplicativo com um cache provider vazio. Aqui está um exemplo com Jest:
+
+```jsx
+describe('test suite', async () => {
+ it('test case', async () => {
+ render(
+ new Map() }}>
+
+
+ )
+ })
+})
+```
+
+### Modify the Cache Data
+
+```jsx
+const { cache } = useSWRConfig()
+
+cache.get(key) // Obtém os dados atuais para uma chave.
+```
+
+
+ você não deve gravar diretamente no cache, isso pode causar um comportamento indefinido.
+
+
+You can use [`mutate`](/docs/mutation) to modify the cache. For example, you can clear all cache data like the following.
+
+```jsx
+const { cache } = useSWRConfig()
+
+mutate(
+ key => true, // which cache keys are updated
+ undefined, // update cache data to `undefined`
+ { revalidate: false } // do not revalidate
+)
+```
+
+More information can be found [here](/docs/arguments#multiple-arguments).
diff --git a/pages/docs/advanced/cache.ru.mdx b/pages/docs/advanced/cache.ru.mdx
index 41047834..53089a34 100644
--- a/pages/docs/advanced/cache.ru.mdx
+++ b/pages/docs/advanced/cache.ru.mdx
@@ -7,8 +7,8 @@ import Callout from 'nextra-theme-docs/callout'
- В большинстве случаев вы не должны напрямую _писать_ в кеш, поскольку это может вызвать
- неопределенное поведение SWR. Если вам нужно вручную изменить ключ, рассмотрите возможность
+ В большинстве случаев вы не должны напрямую _писать_ в кеш, поскольку это может вызвать
+ неопределенное поведение SWR. Если вам нужно вручную изменить ключ, рассмотрите возможность
использования API SWR.
См. также: [Мутация](/docs/mutation), [Сброс кеша между тестами](#сброс-кеша-между-тестами).
@@ -28,6 +28,7 @@ interface Cache {
get(key: string): Data | undefined
set(key: string, value: Data): void
delete(key: string): void
+ keys(): IterableIterator
}
```
@@ -55,7 +56,7 @@ function App() {
другие реализации поставщика кеша для вашего конкретного случая использования.
- В приведённом выше примере, когда компонент ` ` повторно монтируется, провайдер также будет
+ В приведённом выше примере, когда компонент ` ` повторно монтируется, провайдер также будет
повторно создан. Провайдеры кеша должны быть размещены выше в дереве компонентов или вне рендеринга.
@@ -69,7 +70,7 @@ import { Cache } from 'components/diagrams/cache'
верхнего уровня, он возвращается к провайдеру кэша по умолчанию, который является пустым `Map`.
- Если используется провайдер кеша, глобальный `mutate` **не** будет работать для хуков SWR в пределе
+ Если используется провайдер кеша, глобальный `mutate` **не** будет работать для хуков SWR в пределе
этого ``. Пожалуйста, используйте [это](#доступ-к-текущему-провайдеру-кеша) взамен.
@@ -182,8 +183,8 @@ function localStorageProvider() {
```
- В качестве улучшения вы также можете использовать кеш памяти в качестве буфера и периодически
- записывать в `localStorage`. Вы также можете реализовать аналогичный многоуровневый кеш
+ В качестве улучшения вы также можете использовать кеш памяти в качестве буфера и периодически
+ записывать в `localStorage`. Вы также можете реализовать аналогичный многоуровневый кеш
с помощью IndexedDB или WebSQL.
@@ -204,13 +205,28 @@ describe('тестирование', async () => {
})
```
-### Доступ к кешу
-
-Предупреждение: вы не должны писать в кеш напрямую, это может привести к неопределенному поведению.
+### Modify the Cache Data
```jsx
const { cache } = useSWRConfig()
cache.get(key) // Получить текущие данные для ключа.
-cache.clear() // ⚠️ Очистить весь кеш. SWR проведёт ревалидацию при повторном рендеринге.
```
+
+
+ вы не должны писать в кеш напрямую, это может привести к неопределенному поведению.
+
+
+You can use [`mutate`](/docs/mutation) to modify the cache. For example, you can clear all cache data like the following.
+
+```jsx
+const { cache } = useSWRConfig()
+
+mutate(
+ key => true, // which cache keys are updated
+ undefined, // update cache data to `undefined`
+ { revalidate: false } // do not revalidate
+)
+```
+
+More information can be found [here](/docs/arguments#multiple-arguments).
diff --git a/pages/docs/advanced/cache.zh-CN.mdx b/pages/docs/advanced/cache.zh-CN.mdx
index 992ba4aa..2a713f21 100644
--- a/pages/docs/advanced/cache.zh-CN.mdx
+++ b/pages/docs/advanced/cache.zh-CN.mdx
@@ -24,6 +24,7 @@ interface Cache {
get(key: string): Data | undefined
set(key: string, value: Data): void
delete(key: string): void
+ keys(): IterableIterator
}
```
@@ -86,7 +87,7 @@ function Avatar() {
当多个 `` 组件嵌套时,可以扩展缓存 provider。
-`provider` 函数的第一个参数是上层 `` 的缓存 provider(如果没有父级 ``,则为默认缓存),你可以使用它来扩展缓存 provider:
+`provider` 函数的第一个参数是上层 `` 的缓存 provider(如果没有父级 ``,则为默认缓存),你可以使用它来扩展缓存 provider:
```jsx
newCache }}>
@@ -187,13 +188,22 @@ describe('test suite', async () => {
})
```
-### 访问缓存
+### Modify the Cache Data
-警告:你不应该直接写入缓存,那样可能会导致不可预知的行为。
+
+ 你不应该直接写入缓存,那样可能会导致不可预知的行为。
+
+
+You can use [`mutate`](/docs/mutation) to modify the cache. For example, you can clear all cache data like the following.
```jsx
const { cache } = useSWRConfig()
-cache.get(key) // 获取 key 的当前数据。
-cache.clear() // ⚠️ 清除所有缓存。SWR 将在重新渲染时重新请求。
+mutate(
+ key => true, // which cache keys are updated
+ undefined, // update cache data to `undefined`
+ { revalidate: false } // do not revalidate
+)
```
+
+More information can be found [here](/docs/arguments#multiple-arguments).
diff --git a/pages/docs/advanced/devtools.en-US.mdx b/pages/docs/advanced/devtools.en-US.mdx
new file mode 100644
index 00000000..8a6af355
--- /dev/null
+++ b/pages/docs/advanced/devtools.en-US.mdx
@@ -0,0 +1,19 @@
+import Callout from 'nextra-theme-docs/callout'
+
+# DevTools
+
+
+ SWRDevTools is not an official project of Vercel.
+
+
+[SWRDevTools](https://swr-devtools.vercel.app/) is a developer tool for SWR, which helps to debug your SWR cache and fetchers.
+
+
+You can install SWR DevTools from the extension pages and use it with zero settings!
+
+- Chrome: https://chrome.google.com/webstore/detail/swr-devtools/liidbicegefhheghhjbomajjaehnjned
+- Firefox: https://addons.mozilla.org/en-US/firefox/addon/swr-devtools/
+
+After installing it, the SWR devtool panel will appear on browsers' developer tools.
+
+Checkout more information on the [website](https://swr-devtools.vercel.app/) and the [repository](https://github.com/koba04/swr-devtools)
\ No newline at end of file
diff --git a/pages/docs/advanced/devtools.es-ES.mdx b/pages/docs/advanced/devtools.es-ES.mdx
new file mode 100644
index 00000000..ee6bc7ba
--- /dev/null
+++ b/pages/docs/advanced/devtools.es-ES.mdx
@@ -0,0 +1,20 @@
+import Callout from 'nextra-theme-docs/callout'
+
+# DevTools
+
+
+
+ SWRDevTools is not an official project of Vercel.
+
+
+[SWRDevTools](https://swr-devtools.vercel.app/) is a developer tool for SWR, which helps to debug your SWR cache and fetchers.
+
+
+You can install SWR DevTools from the extension pages and use it with zero settings!
+
+- Chrome: https://chrome.google.com/webstore/detail/swr-devtools/liidbicegefhheghhjbomajjaehnjned
+- Firefox: https://addons.mozilla.org/en-US/firefox/addon/swr-devtools/
+
+After installing it, the SWR devtool panel will appear on browsers' developer tools.
+
+Checkout more information on the [website](https://swr-devtools.vercel.app/) and the [repository](https://github.com/koba04/swr-devtools)
\ No newline at end of file
diff --git a/pages/docs/advanced/devtools.ja.mdx b/pages/docs/advanced/devtools.ja.mdx
new file mode 100644
index 00000000..80069dd3
--- /dev/null
+++ b/pages/docs/advanced/devtools.ja.mdx
@@ -0,0 +1,19 @@
+import Callout from 'nextra-theme-docs/callout'
+
+# 開発者ツール
+
+
+
+ SWRDevTools は Vercel 公式プロジェクトではありません。
+
+
+[SWRDevTools](https://swr-devtools.vercel.app/) は SWR のための開発者ツールで、SWR のキャッシュやフェッチの状態のデバッグに役に立ちます。
+
+SWR DevTools はブラウザ拡張のページからインストールするだけで、追加の設定なしに使うことができます。
+
+- Chrome: https://chrome.google.com/webstore/detail/swr-devtools/liidbicegefhheghhjbomajjaehnjned
+- Firefox: https://addons.mozilla.org/en-US/firefox/addon/swr-devtools/
+
+インストール後、SWR 用のパネルがブラウザの開発者ツールに追加されます。
+
+機能については SWRDevTools の [ウェブサイト](https://swr-devtools.vercel.app/) と [リポジトリ](https://github.com/koba04/swr-devtools) を確認してください。
diff --git a/pages/docs/advanced/devtools.ko.mdx b/pages/docs/advanced/devtools.ko.mdx
new file mode 100644
index 00000000..ee6bc7ba
--- /dev/null
+++ b/pages/docs/advanced/devtools.ko.mdx
@@ -0,0 +1,20 @@
+import Callout from 'nextra-theme-docs/callout'
+
+# DevTools
+
+
+
+ SWRDevTools is not an official project of Vercel.
+
+
+[SWRDevTools](https://swr-devtools.vercel.app/) is a developer tool for SWR, which helps to debug your SWR cache and fetchers.
+
+
+You can install SWR DevTools from the extension pages and use it with zero settings!
+
+- Chrome: https://chrome.google.com/webstore/detail/swr-devtools/liidbicegefhheghhjbomajjaehnjned
+- Firefox: https://addons.mozilla.org/en-US/firefox/addon/swr-devtools/
+
+After installing it, the SWR devtool panel will appear on browsers' developer tools.
+
+Checkout more information on the [website](https://swr-devtools.vercel.app/) and the [repository](https://github.com/koba04/swr-devtools)
\ No newline at end of file
diff --git a/pages/docs/advanced/devtools.pt-BR.mdx b/pages/docs/advanced/devtools.pt-BR.mdx
new file mode 100644
index 00000000..ee6bc7ba
--- /dev/null
+++ b/pages/docs/advanced/devtools.pt-BR.mdx
@@ -0,0 +1,20 @@
+import Callout from 'nextra-theme-docs/callout'
+
+# DevTools
+
+
+
+ SWRDevTools is not an official project of Vercel.
+
+
+[SWRDevTools](https://swr-devtools.vercel.app/) is a developer tool for SWR, which helps to debug your SWR cache and fetchers.
+
+
+You can install SWR DevTools from the extension pages and use it with zero settings!
+
+- Chrome: https://chrome.google.com/webstore/detail/swr-devtools/liidbicegefhheghhjbomajjaehnjned
+- Firefox: https://addons.mozilla.org/en-US/firefox/addon/swr-devtools/
+
+After installing it, the SWR devtool panel will appear on browsers' developer tools.
+
+Checkout more information on the [website](https://swr-devtools.vercel.app/) and the [repository](https://github.com/koba04/swr-devtools)
\ No newline at end of file
diff --git a/pages/docs/advanced/devtools.ru.mdx b/pages/docs/advanced/devtools.ru.mdx
new file mode 100644
index 00000000..ee6bc7ba
--- /dev/null
+++ b/pages/docs/advanced/devtools.ru.mdx
@@ -0,0 +1,20 @@
+import Callout from 'nextra-theme-docs/callout'
+
+# DevTools
+
+
+
+ SWRDevTools is not an official project of Vercel.
+
+
+[SWRDevTools](https://swr-devtools.vercel.app/) is a developer tool for SWR, which helps to debug your SWR cache and fetchers.
+
+
+You can install SWR DevTools from the extension pages and use it with zero settings!
+
+- Chrome: https://chrome.google.com/webstore/detail/swr-devtools/liidbicegefhheghhjbomajjaehnjned
+- Firefox: https://addons.mozilla.org/en-US/firefox/addon/swr-devtools/
+
+After installing it, the SWR devtool panel will appear on browsers' developer tools.
+
+Checkout more information on the [website](https://swr-devtools.vercel.app/) and the [repository](https://github.com/koba04/swr-devtools)
\ No newline at end of file
diff --git a/pages/docs/advanced/devtools.zh-CN.mdx b/pages/docs/advanced/devtools.zh-CN.mdx
new file mode 100644
index 00000000..ee6bc7ba
--- /dev/null
+++ b/pages/docs/advanced/devtools.zh-CN.mdx
@@ -0,0 +1,20 @@
+import Callout from 'nextra-theme-docs/callout'
+
+# DevTools
+
+
+
+ SWRDevTools is not an official project of Vercel.
+
+
+[SWRDevTools](https://swr-devtools.vercel.app/) is a developer tool for SWR, which helps to debug your SWR cache and fetchers.
+
+
+You can install SWR DevTools from the extension pages and use it with zero settings!
+
+- Chrome: https://chrome.google.com/webstore/detail/swr-devtools/liidbicegefhheghhjbomajjaehnjned
+- Firefox: https://addons.mozilla.org/en-US/firefox/addon/swr-devtools/
+
+After installing it, the SWR devtool panel will appear on browsers' developer tools.
+
+Checkout more information on the [website](https://swr-devtools.vercel.app/) and the [repository](https://github.com/koba04/swr-devtools)
\ No newline at end of file
diff --git a/pages/docs/advanced/meta.en-US.json b/pages/docs/advanced/meta.en-US.json
index 616f2469..eaa2031d 100644
--- a/pages/docs/advanced/meta.en-US.json
+++ b/pages/docs/advanced/meta.en-US.json
@@ -1,5 +1,7 @@
{
+ "understanding": "Understanding SWR",
"cache": "Cache",
"performance": "Performance",
- "react-native": "React Native"
+ "react-native": "React Native",
+ "devtools": "Developer Tools"
}
diff --git a/pages/docs/advanced/meta.es-ES.json b/pages/docs/advanced/meta.es-ES.json
index 210f6d38..853998cd 100644
--- a/pages/docs/advanced/meta.es-ES.json
+++ b/pages/docs/advanced/meta.es-ES.json
@@ -1,5 +1,7 @@
{
+ "understanding": "Understanding SWR",
"cache": "Cache",
"performance": "Rendimiento",
- "react-native": "React Native"
+ "react-native": "React Native",
+ "devtools": "Developer Tools"
}
diff --git a/pages/docs/advanced/meta.ja.json b/pages/docs/advanced/meta.ja.json
index d7537302..3766dc5a 100644
--- a/pages/docs/advanced/meta.ja.json
+++ b/pages/docs/advanced/meta.ja.json
@@ -1,5 +1,7 @@
{
+ "understanding": "SWR を理解する",
"cache": "キャッシュ",
"performance": "パフォーマンス",
- "react-native": "React Native"
+ "react-native": "React Native",
+ "devtools": "開発者ツール"
}
diff --git a/pages/docs/advanced/meta.ko.json b/pages/docs/advanced/meta.ko.json
index b513a463..c13b5d15 100644
--- a/pages/docs/advanced/meta.ko.json
+++ b/pages/docs/advanced/meta.ko.json
@@ -1,5 +1,8 @@
{
+ "understanding": "Understanding SWR",
"cache": "캐시",
"performance": "성능",
- "react-native": "React Native"
+ "react-native": "React Native",
+ "devtools": "Developer Tools"
+
}
diff --git a/pages/docs/advanced/meta.pt-BR.json b/pages/docs/advanced/meta.pt-BR.json
index fe42a551..02c5de4f 100644
--- a/pages/docs/advanced/meta.pt-BR.json
+++ b/pages/docs/advanced/meta.pt-BR.json
@@ -1,5 +1,7 @@
-{
- "cache": "Cache",
- "performance": "Desempenho",
- "react-native": "React Native"
-}
+{
+ "understanding": "Understanding SWR",
+ "cache": "Cache",
+ "performance": "Desempenho",
+ "react-native": "React Native",
+ "devtools": "Developer Tools"
+}
diff --git a/pages/docs/advanced/meta.ru.json b/pages/docs/advanced/meta.ru.json
index b76fa617..6587de69 100644
--- a/pages/docs/advanced/meta.ru.json
+++ b/pages/docs/advanced/meta.ru.json
@@ -1,5 +1,7 @@
{
+ "understanding": "Understanding SWR",
"cache": "Кеш",
"performance": "Производительность",
- "react-native": "React Native"
+ "react-native": "React Native",
+ "devtools": "Developer Tools"
}
diff --git a/pages/docs/advanced/meta.zh-CN.json b/pages/docs/advanced/meta.zh-CN.json
index ece5ba45..352bc5f2 100644
--- a/pages/docs/advanced/meta.zh-CN.json
+++ b/pages/docs/advanced/meta.zh-CN.json
@@ -1,5 +1,7 @@
{
+ "understanding": "Understanding SWR",
"cache": "缓存",
"performance": "性能",
- "react-native": "React Native"
+ "react-native": "React Native",
+ "devtools": "Developer Tools"
}
diff --git a/pages/docs/advanced/performance.en-US.mdx b/pages/docs/advanced/performance.en-US.mdx
index e8e3c797..04a2c3ad 100644
--- a/pages/docs/advanced/performance.en-US.mdx
+++ b/pages/docs/advanced/performance.en-US.mdx
@@ -46,24 +46,24 @@ Each `` component has a `useSWR` hook inside. Since they have the same S
You can reuse your data hooks (like `useUser` in the example above) everywhere, without worrying about performance or duplicated requests.
-There is also a [`dedupingInterval` option](/docs/options) for overriding the default deduplication interval.
+There is also a [`dedupingInterval` option](/docs/api) for overriding the default deduplication interval.
## Deep Comparison
SWR **deep compares** data changes by default. If the `data` value isn’t changed, a re-render will not be triggered.
-You can also customize the comparison function via the [`compare` option](/docs/options) if you want to change the behavior.
+You can also customize the comparison function via the [`compare` option](/docs/api) if you want to change the behavior.
For example, some API responses return a server timestamp that you might want to exclude from the data diff.
## Dependency Collection
-`useSWR` returns 3 **stateful** values: `data`, `error` and `isValidating`, each one can be updated independently.
+`useSWR` returns 4 **stateful** values: `data`, `error`, `isLoading` and `isValidating`, each one can be updated independently.
For example, if we print those values within a full data-fetching lifecycle, it will be something like this:
```jsx
function App () {
- const { data, error, isValidating } = useSWR('/api', fetcher)
- console.log(data, error, isValidating)
+ const { data, error, isLoading, isValidating } = useSWR('/api', fetcher)
+ console.log(data, error, isLoading, isValidating)
return null
}
```
@@ -71,11 +71,11 @@ function App () {
In the worst case (the first request failed, then the retry was successful), you will see 4 lines of logs:
```js
-// console.log(data, error, isValidating)
-undefined undefined true // => start fetching
-undefined Error false // => end fetching, got an error
-undefined Error true // => start retrying
-Data undefined false // => end retrying, get the data
+// console.log(data, error, isLoading, isValidating)
+undefined undefined true true // => start fetching
+undefined Error false false // => end fetching, got an error
+undefined Error true true // => start retrying
+Data undefined false false // => end retrying, get the data
```
The state changes make sense. But that also means our component **rendered 4 times**.
diff --git a/pages/docs/advanced/performance.es-ES.mdx b/pages/docs/advanced/performance.es-ES.mdx
index e0a9af35..8f80d5fb 100644
--- a/pages/docs/advanced/performance.es-ES.mdx
+++ b/pages/docs/advanced/performance.es-ES.mdx
@@ -46,32 +46,32 @@ function App() {
```
-Cada componente ` ` tiene un hook `useSWR` en su interior. Dado que tienen el mismo key SWR y
+Cada componente ` ` tiene un hook `useSWR` en su interior. Dado que tienen el mismo key SWR y
que se renderizan casi al mismo tiempo, **sólo se hará 1 solicitud de red**.
Se pueden reutilizar los hooks de datos (como `useUser` en el ejemplo anterior) en todas partes, sin preocuparse por el rendimiento
o las peticiones duplicadas.
-También existe la [opción `dedupingInterval`](/docs/options) para anular el intervalo de deduplicación por defecto.
+También existe la [opción `dedupingInterval`](/docs/api) para anular el intervalo de deduplicación por defecto.
## Comparación profunda
SWR por defecto tiene **deep compares** al cambio de datos. Si el valor de `data` no ha cambiado, no se
activará una nueva renderización.
-También puede personalizar la función de comparación mediante la [opción `compare`](/docs/options) si quieres cambiar el comportamiento.
+También puede personalizar la función de comparación mediante la [opción `compare`](/docs/api) si quieres cambiar el comportamiento.
Por ejemplo, algunas respuestas de la API devuelven una marca de tiempo del servidor que tal vez quiera excluir de la difusión de datos.
## Colección de dependencias
-`useSWR` devuelve 3 valores de **estado**: `data`, `error` y `isValidating` cada uno de ellos puede actualizarse de forma independientemente.
+`useSWR` devuelve 4 valores de **estado**: `data`, `error`, `isLoading` y `isValidating` cada uno de ellos puede actualizarse de forma independientemente.
Por ejemplo, si imprimimos esos valores dentro de un ciclo de vida completo de obtención de datos, será algo como esto:
```jsx
function App () {
- const { data, error, isValidating } = useSWR('/api', fetcher)
- console.log(data, error, isValidating)
+ const { data, error, isLoading, isValidating } = useSWR('/api', fetcher)
+ console.log(data, error, isLoading, isValidating)
return null
}
@@ -80,12 +80,12 @@ function App () {
En el peor de los casos (si la primera solicitud falló, entonces el reintento fue exitoso). Se verán 4 líneas de registros:
```js
-// console.log(data, error, isValidating)
+// console.log(data, error, isLoading, isValidating)
-undefined undefined true // => start fetching
-undefined Error false // => end fetching, got an error
-undefined Error true // => start retrying
-Data undefined false // => end retrying, get the data
+undefined undefined true true // => start fetching
+undefined Error false false // => end fetching, got an error
+undefined Error true true // => start retrying
+Data undefined false false // => end retrying, get the data
```
Los cambios de estado tienen sentido. Pero eso también significa que nuestro componente se **renderizo 4 veces.**
@@ -112,7 +112,7 @@ Data // => end retrying, get the data
El mismo proceso ha ocurrido internamente, hubo un error de la primera solicitud, entonces tenemos los datos del reintento.
Sin embargo, **SWR sólo actualiza los estados que utiliza el componente**, que ahora sólo es `data`.
-Si no se utilizan siempre estos 3 estados, ya se está beneficiando de esta función. En [Vercel](https://vercel.com), esta optimización se
+Si no se utilizan siempre estos 3 estados, ya se está beneficiando de esta función. En [Vercel](https://vercel.com), esta optimización se
traduce en un 60% menos de repeticiones.
## Tree Shaking
diff --git a/pages/docs/advanced/performance.ja.mdx b/pages/docs/advanced/performance.ja.mdx
index 3da27c82..2adf567d 100644
--- a/pages/docs/advanced/performance.ja.mdx
+++ b/pages/docs/advanced/performance.ja.mdx
@@ -46,24 +46,24 @@ function App () {
パフォーマンスや重複したリクエストを心配することなく、どこでも(上記の例にあった `useUser` のように)データフックを再利用できます。
-デフォルトの重複排除間隔を上書きする [`dedupingInterval`](/docs/options) オプションもあります。
+デフォルトの重複排除間隔を上書きする [`dedupingInterval`](/docs/api) オプションもあります。
## 詳細な比較
SWR は、デフォルトでデータの変更を**詳細に比較**します。`data` という値が変更されていない場合、再レンダリングは開始されません。
-動作を変更したい場合は、[`compare` オプション](/docs/options)を使用して比較機能をカスタマイズすることもできます。
+動作を変更したい場合は、[`compare` オプション](/docs/api)を使用して比較機能をカスタマイズすることもできます。
たとえば、一部の API レスポンスは、データ差分から除外したいサーバーのタイムスタンプを返します。
## 依存関係のコレクション
-`useSWR` が返す三つの**ステートフルな**値、つまり `data` 、`error` 、そして `isValidating` は、それぞれが独立して更新されます。
+`useSWR` が返す四つの**ステートフルな**値、つまり `data` 、`error` `isLoading`、そして `isValidating` は、それぞれが独立して更新されます。
たとえば、完全なデータフェッチのライフサイクル内でこれらの値を出力すると次のようになります:
```jsx
function App () {
- const { data, error, isValidating } = useSWR('/api', fetcher)
- console.log(data, error, isValidating)
+ const { data, error, isLoading, isValidating } = useSWR('/api', fetcher)
+ console.log(data, error, isLoading, isValidating)
return null
}
```
@@ -71,11 +71,11 @@ function App () {
最悪の場合(最初のリクエストが失敗し、次に再試行が成功した場合)には、4 行のログが表示されます:
```js
-// console.log(data, error, isValidating)
-undefined undefined true // => フェッチの開始
-undefined Error false // => フェッチの完了、エラーを取得
-undefined Error true // => 再試行の開始
-Data undefined false // => 再試行の完了、データを取得
+// console.log(data, error, isLoading, isValidating)
+undefined undefined true true // => フェッチの開始
+undefined Error false false // => フェッチの完了、エラーを取得
+undefined Error false true // => 再試行の開始
+Data undefined false false // => 再試行の完了、データを取得
```
この状態変化は理にかなっています。しかし、それはまた、コンポーネントが **4 回レンダリングされる**ことを意味します。
diff --git a/pages/docs/advanced/performance.ko.mdx b/pages/docs/advanced/performance.ko.mdx
index 83c3cd3e..81c35d7f 100644
--- a/pages/docs/advanced/performance.ko.mdx
+++ b/pages/docs/advanced/performance.ko.mdx
@@ -2,7 +2,7 @@
SWR은 모든 종류의 웹 앱에서 중요한 기능들을 제공하므로 **성능**을 가장 중요시합니다.
-SWR의 내장 **캐싱**과 **[중복 제거](/advanced/performance#deduplication)** 기능은 불필요한 네트워크 요청을 생략하지만,
+SWR의 내장 **캐싱**과 **[중복 제거](/advanced/performance#deduplication)** 기능은 불필요한 네트워크 요청을 생략하지만,
`useSWR` hook 자체의 성능은 여전히 중요합니다. 복잡한 앱에서는 단일 페이지 렌더링에 `useSWR`이 수백 번 호출될 수 있습니다.
SWR은 앱에서 다음을 보장합니다:
@@ -14,7 +14,7 @@ SWR은 앱에서 다음을 보장합니다:
## 중복 제거
-앱에서 SWR hook을 재사용하는 것은 아주 일반적입니다. 예를 들어, 앱이 현재 사용자의 아바타를 다섯 번 렌더링한다면:
+앱에서 SWR hook을 재사용하는 것은 아주 일반적입니다. 예를 들어, 앱이 현재 사용자의 아바타를 다섯 번 렌더링한다면:
```jsx
function useUser () {
@@ -45,24 +45,24 @@ function App () {
성능이나 중복된 요청에 대한 걱정 없이 위 예시의 `useUser`와 같은 데이터 hook을 어디에서든 재사용할 수 있습니다.
-기본 중복 제거 인터벌을 오버라이딩할 수 있는 [`dedupingInterval` 옵션](/docs/options)도 있습니다.
+기본 중복 제거 인터벌을 오버라이딩할 수 있는 [`dedupingInterval` 옵션](/docs/api)도 있습니다.
## 깊은 비교
SWR은 기본적으로 데이터 변경을 **깊게 비교**합니다. `data` 값이 변경되지 않았다면 리렌더링을 트리거 하지 않습니다.
-이 동작을 변경하길 원한다면 [`compare` 옵션](/docs/options)을 통해 비교 함수를 커스터마이징할 수도 있습니다.
+이 동작을 변경하길 원한다면 [`compare` 옵션](/docs/api)을 통해 비교 함수를 커스터마이징할 수도 있습니다.
예를 들면, 일부 API의 응답이 데이터 diff에서 제외하길 원하는 서버의 타임 스탬프를 반환합니다.
## 의존성 수집
-`useSWR`은 세 개의 **스테이트풀** 값을 반환합니다: `data`, `error`, `isValidating`, 각각은 독립적으로 업데이트됩니다.
+`useSWR` returns 4 **stateful** values: `data`, `error`, `isLoading` and `isValidating`, each one can be updated independently.
예를 들어, 전체 데이터 가져오기 생명 주기에서 이 값들을 출력한다면 뭔가 다음과 같은 것입니다.
```jsx
function App () {
- const { data, error, isValidating } = useSWR('/api', fetcher)
- console.log(data, error, isValidating)
+ const { data, error, isLoading, isValidating } = useSWR('/api', fetcher)
+ console.log(data, error, isLoading, isValidating)
return null
}
```
@@ -70,11 +70,11 @@ function App () {
최악의 경우에는(첫 번째 요청이 실패하고, 재시도가 성공), 네 줄의 로그가 보일 것입니다.
```js
-// console.log(data, error, isValidating)
-undefined undefined true // => 가져오기 시작
-undefined Error false // => 가져오기 종료, 에러 발생
-undefined Error true // => 재시도 시작
-Data undefined false // => 재시도 종료, 데이터 얻음
+// console.log(data, error, isLoading, isValidating)
+undefined undefined true true // => 가져오기 시작
+undefined Error false false // => 가져오기 종료, 에러 발생
+undefined Error true true // => 재시도 시작
+Data undefined false false // => 재시도 종료, 데이터 얻음
```
상태 변화는 말이 됩니다. 하지만 이는 컴포넌트가 **네 번 리렌더링 되었음**을 의미하기도 합니다.
diff --git a/pages/docs/advanced/performance.pt-BR.mdx b/pages/docs/advanced/performance.pt-BR.mdx
index 809fdccf..d694db0b 100644
--- a/pages/docs/advanced/performance.pt-BR.mdx
+++ b/pages/docs/advanced/performance.pt-BR.mdx
@@ -1,110 +1,110 @@
-# Desempenho
-
-O SWR oferece funcionalidades essenciais em todos os tipos de aplicações da web, portanto, o **desempenho** é a principal prioridade.
-
-O **caching** e a **[desduplicação](#deduplication)** integrados do SWR ignoram solicitações de rede desnecessárias, mas
-o desempenho do hook `useSWR` ainda é importante. Em uma aplicação complexa, pode haver centenas de chamadas de `useSWR` em uma única renderização de página.
-
-O SWR garante que seu aplicativo tenha:
-
-- _sem solicitações desnecessárias_
-- _sem re-renderizações desnecessárias_
-- _nenhum código desnecessário importado_
-
-sem qualquer alteração de código feita por você.
-
-## Desduplicação
-
-É muito comum reutilizar hooks SWR em seu aplicativo. Por exemplo, uma aplicação que renderiza o avatar do usuário atual 5 vezes:
-
-```jsx
-function useUser () {
- return useSWR('/api/user', fetcher)
-}
-
-function Avatar () {
- const { data, error } = useUser()
-
- if (error) return
- if (!data) return
-
- return
-}
-
-function App () {
- return <>
-
-
-
-
-
- >
-}
-```
-
-Cada componente `` tem um hook `useSWR` dentro. Como eles têm a mesma chave SWR e são renderizados quase ao mesmo tempo, **apenas 1 solicitação de rede será feita**.
-
-Você pode reutilizar seus hooks de dados (como `useUser` no exemplo acima) em qualquer lugar, sem se preocupar com desempenho ou solicitações duplicadas.
-
-Há também uma [opção `dedupingInterval`](/docs/options) para substituir o intervalo de desduplicação padrão.
-
-## Comparação Profunda
-
-SWR **compara profundamente** as mudanças de dados por padrão. Se o valor `data` não é alterado, uma re-renderização não será acionada.
-
-Você também pode customizar a função de comparação via opção [`compare`](/docs/options) se você quiser mudar o comportamento.
-Por exemplo, alguns respostas de API retornam um timestamp do servidor que você pode querer excluir do diff de dados.
-
-## Coleção de Dependências
-
-`useSWR` retorna 3 valores **stateful**: `data`, `error` e `isValidating`, cada um pode ser atualizado independentemente.
-Por exemplo, se imprimirmos esses valores em um ciclo de vida completo de busca de dados, será algo assim:
-
-```jsx
-function App () {
- const { data, error, isValidating } = useSWR('/api', fetcher)
- console.log(data, error, isValidating)
- return null
-}
-```
-
-Na pior das hipóteses (a primeira solicitação falhou e a tentativa foi bem-sucedida), você verá 4 linhas de logs:
-
-```js
-// console.log(data, error, isValidating)
-undefined undefined true // => começar fetching
-undefined Error false // => terminar fetching, recebeu um erro
-undefined Error true // => começar retrying
-Data undefined false // => terminar retrying, recebeu um erro
-```
-
-As mudanças de estado fazem sentido. Mas isso também significa que nosso componente **renderizou 4 vezes**.
-
-Se alterarmos nosso componente para usar apenas `data`:
-
-```jsx
-function App () {
- const { data } = useSWR('/api', fetcher)
- console.log(data)
- return null
-}
-```
-
-A mágica acontece: há apenas **2 rerenderizações** agora:
-
-```js
-// console.log(data)
-undefined // => hidratação / renderização inciial
-Data // => para de tentar, recebe os dados
-```
-
-Exatamente o mesmo processo aconteceu internamente, houve um erro desde a primeira solicitação, então obtivemos os dados da nova tentativa.
-No entanto, **SWR atualiza apenas os estados que são usados pelo componente**, que agora são apenas `data`.
-
-Se você nem sempre está usando todos esses 3 estados, já está se beneficiando desse recurso.
-Na [Vercel](https://vercel.com), essa otimização resulta em aproximadamente 60% menos re-renderizações.
-
-## Tree Shaking
-
-O pacote SWR é [tree-shakeable](https://webpack.js.org/guides/tree-shaking) e possuem side-effect tree.
-Isso significa que se você estiver importando apenas a API principal `useSWR`, APIs não utilizadas como `useSWRInfinite` não serão empacotadas em seu aplicativo.
+# Desempenho
+
+O SWR oferece funcionalidades essenciais em todos os tipos de aplicações da web, portanto, o **desempenho** é a principal prioridade.
+
+O **caching** e a **[desduplicação](#deduplication)** integrados do SWR ignoram solicitações de rede desnecessárias, mas
+o desempenho do hook `useSWR` ainda é importante. Em uma aplicação complexa, pode haver centenas de chamadas de `useSWR` em uma única renderização de página.
+
+O SWR garante que seu aplicativo tenha:
+
+- _sem solicitações desnecessárias_
+- _sem re-renderizações desnecessárias_
+- _nenhum código desnecessário importado_
+
+sem qualquer alteração de código feita por você.
+
+## Desduplicação
+
+É muito comum reutilizar hooks SWR em seu aplicativo. Por exemplo, uma aplicação que renderiza o avatar do usuário atual 5 vezes:
+
+```jsx
+function useUser () {
+ return useSWR('/api/user', fetcher)
+}
+
+function Avatar () {
+ const { data, error } = useUser()
+
+ if (error) return
+ if (!data) return
+
+ return
+}
+
+function App () {
+ return <>
+
+
+
+
+
+ >
+}
+```
+
+Cada componente `` tem um hook `useSWR` dentro. Como eles têm a mesma chave SWR e são renderizados quase ao mesmo tempo, **apenas 1 solicitação de rede será feita**.
+
+Você pode reutilizar seus hooks de dados (como `useUser` no exemplo acima) em qualquer lugar, sem se preocupar com desempenho ou solicitações duplicadas.
+
+Há também uma [opção `dedupingInterval`](/docs/api) para substituir o intervalo de desduplicação padrão.
+
+## Comparação Profunda
+
+SWR **compara profundamente** as mudanças de dados por padrão. Se o valor `data` não é alterado, uma re-renderização não será acionada.
+
+Você também pode customizar a função de comparação via opção [`compare`](/docs/api) se você quiser mudar o comportamento.
+Por exemplo, alguns respostas de API retornam um timestamp do servidor que você pode querer excluir do diff de dados.
+
+## Coleção de Dependências
+
+`useSWR` retorna 4 valores **stateful**: `data`, `error`, `isLoading` e `isValidating`, cada um pode ser atualizado independentemente.
+Por exemplo, se imprimirmos esses valores em um ciclo de vida completo de busca de dados, será algo assim:
+
+```jsx
+function App () {
+ const { data, error, isLoading, isValidating } = useSWR('/api', fetcher)
+ console.log(data, error, isLoading, isValidating)
+ return null
+}
+```
+
+Na pior das hipóteses (a primeira solicitação falhou e a tentativa foi bem-sucedida), você verá 4 linhas de logs:
+
+```js
+// console.log(data, error, isLoading, isValidating)
+undefined undefined true true // => começar fetching
+undefined Error false false // => terminar fetching, recebeu um erro
+undefined Error true true // => começar retrying
+Data undefined false false // => terminar retrying, recebeu um erro
+```
+
+As mudanças de estado fazem sentido. Mas isso também significa que nosso componente **renderizou 4 vezes**.
+
+Se alterarmos nosso componente para usar apenas `data`:
+
+```jsx
+function App () {
+ const { data } = useSWR('/api', fetcher)
+ console.log(data)
+ return null
+}
+```
+
+A mágica acontece: há apenas **2 rerenderizações** agora:
+
+```js
+// console.log(data)
+undefined // => hidratação / renderização inciial
+Data // => para de tentar, recebe os dados
+```
+
+Exatamente o mesmo processo aconteceu internamente, houve um erro desde a primeira solicitação, então obtivemos os dados da nova tentativa.
+No entanto, **SWR atualiza apenas os estados que são usados pelo componente**, que agora são apenas `data`.
+
+Se você nem sempre está usando todos esses 3 estados, já está se beneficiando desse recurso.
+Na [Vercel](https://vercel.com), essa otimização resulta em aproximadamente 60% menos re-renderizações.
+
+## Tree Shaking
+
+O pacote SWR é [tree-shakeable](https://webpack.js.org/guides/tree-shaking) e possuem side-effect tree.
+Isso significa que se você estiver importando apenas a API principal `useSWR`, APIs não utilizadas como `useSWRInfinite` não serão empacotadas em seu aplicativo.
diff --git a/pages/docs/advanced/performance.ru.mdx b/pages/docs/advanced/performance.ru.mdx
index d62845e5..27b6bb63 100644
--- a/pages/docs/advanced/performance.ru.mdx
+++ b/pages/docs/advanced/performance.ru.mdx
@@ -51,7 +51,7 @@ function App () {
Вы можете повторно использовать свои хуки данных (например, `useUser` в приведенном выше примере) повсюду,
не беспокоясь о производительности или дублировании запросов.
-Существует также [опция `dedupingInterval`](/docs/options) для переопределения интервала дедупликации
+Существует также [опция `dedupingInterval`](/docs/api) для переопределения интервала дедупликации
по умолчанию.
## Глубокое сравнение
@@ -59,20 +59,20 @@ function App () {
SWR **глубоко сравнивает** изменения данных по умолчанию. Если значение `data` не изменилось,
повторный рендеринг запускаться не будет.
-Вы также можете настроить функцию сравнения с помощью [опции `compare`](/docs/options), если хотите
+Вы также можете настроить функцию сравнения с помощью [опции `compare`](/docs/api), если хотите
изменить поведение. Например, некоторые ответы API возвращают отметку времени сервера, которую вы,
возможно, захотите исключить из сравнения данных.
## Коллекция зависимостей
-`useSWR` возвращает 3 значения **с сохранением состояния**: `data`, `error` и `isValidating`,
+`useSWR` возвращает 4 значения **с сохранением состояния**: `data`, `error`, `isLoading` и `isValidating`,
каждое из которых может обновляться независимо. Например, если мы выведим эти значения внутри
полного жизненного цикла выборки данных, это будет примерно так:
```jsx
function App () {
- const { data, error, isValidating } = useSWR('/api', fetcher)
- console.log(data, error, isValidating)
+ const { data, error, isLoading, isValidating } = useSWR('/api', fetcher)
+ console.log(data, error, isLoading, isValidating)
return null
}
```
@@ -80,11 +80,11 @@ function App () {
В худшем случае (первый запрос не удался, затем повторная попытка была успешной) вы увидите 4 строки журнала:
```js
-// console.log(data, error, isValidating)
-undefined undefined true // => начало выборки
-undefined Error false // => конец выборки, получили ошибку
-undefined Error true // => начало повторной попытки
-Data undefined false // => конец повторной попытки, получение данных
+// console.log(data, error, isLoading, isValidating)
+undefined undefined true true // => начало выборки
+undefined Error false false // => конец выборки, получили ошибку
+undefined Error true true // => начало повторной попытки
+Data undefined false false // => конец повторной попытки, получение данных
```
Изменения состояния имеют смысл. Но это также означает, что наш компонент **рендерился 4 раза**.
diff --git a/pages/docs/advanced/performance.zh-CN.mdx b/pages/docs/advanced/performance.zh-CN.mdx
index d0e1eb45..e15796b0 100644
--- a/pages/docs/advanced/performance.zh-CN.mdx
+++ b/pages/docs/advanced/performance.zh-CN.mdx
@@ -44,22 +44,22 @@ function App () {
你可以在任何地方重用数据 hooks(比如上面示例中的 `useUser`),而不用担心性能或重复请求。
-还有一个 [`dedupingInterval` 选项](/docs/options) 用于覆盖默认的重复数据删除间隔。
+还有一个 [`dedupingInterval` 选项](/docs/api) 用于覆盖默认的重复数据删除间隔。
## 深度比较
SWR 默认 **深度比较** 数据更改。如果 `data` 值没有改变,则不会触发重新渲染。
-如果你还想更改的话,可以通过 [`compare` 选项](/docs/options) 自定义比较函数。比如,某些 API 响应返回一个服务器时间戳,你可能想从数据 diff 中排除它。
+如果你还想更改的话,可以通过 [`compare` 选项](/docs/api) 自定义比较函数。比如,某些 API 响应返回一个服务器时间戳,你可能想从数据 diff 中排除它。
## 依赖收集
-`useSWR` 返回 3 个 **有状态的** 值:`data`、`error` 和 `isValidating`,每个都可以独立更新。例如,如果我们在一个完整的数据请求生命周期中打印这些值,则将如下所示:
+`useSWR` 返回 4 个 **有状态的** 值:`data`、`error`、`isLoading`和 `isValidating`,每个都可以独立更新。例如,如果我们在一个完整的数据请求生命周期中打印这些值,则将如下所示:
```jsx
function App () {
- const { data, error, isValidating } = useSWR('/api', fetcher)
- console.log(data, error, isValidating)
+ const { data, error, isLoading, isValidating } = useSWR('/api', fetcher)
+ console.log(data, error, isLoading, isValidating)
return null
}
```
@@ -67,11 +67,11 @@ function App () {
在最坏的情况下(第一个请求失败,然后重试成功),你将看到 4 行日志:
```js
-// console.log(data, error, isValidating)
-undefined undefined true // => 开始 fetching
-undefined Error false // => 结束 fetching,出现错误
-undefined Error true // => 开始重试
-Data undefined false // => 重试结束,得到数据
+// console.log(data, error, isLoading, isValidating)
+undefined undefined true true // => 开始 fetching
+undefined Error false false // => 结束 fetching,出现错误
+undefined Error true true // => 开始重试
+Data undefined false false // => 重试结束,得到数据
```
状态的改变是有道理的。但这也意味着组件 **渲染了 4 次**。
@@ -94,7 +94,7 @@ undefined // => hydration / 初始渲染
Data // => 重试结束,得到数据
```
-内部发生了完全相同的过程,第一个请求出现了错误,然后我们重试得到了数据。但是,**SWR 只更新了组件使用的状态**,即:`data`。
+内部发生了完全相同的过程,第一个请求出现了错误,然后我们重试得到了数据。但是,**SWR 只更新了组件使用的状态**,即:`data`。
如果你不是总使用这3种状态,那么你已经从这个特性中获益了。在 [Vercel](https://vercel.com) 的实际应用中,这个优化减少了约 60% 的重新渲染。
diff --git a/pages/docs/advanced/understanding.en-US.mdx b/pages/docs/advanced/understanding.en-US.mdx
new file mode 100644
index 00000000..98e1c1b9
--- /dev/null
+++ b/pages/docs/advanced/understanding.en-US.mdx
@@ -0,0 +1,125 @@
+import Callout from 'nextra-theme-docs/callout'
+import Video from 'components/video'
+
+# Understanding SWR
+
+## State Machine
+
+`useSWR` returns `data`, `error`, `isLoading`, and `isValidating` depending on the state of the `fetcher` function. This diagrams describe how SWR returns values in some scenarios.
+
+### Fetch and Revalidate
+
+This pattern is to fetch data and revalidate it later.
+
+![A pattern for fetch and revalidate](/img/understanding/fetch-and-revalidate.svg)
+
+### Key Change
+
+This pattern is to fetch data and change the key and revalidate it later.
+
+![A pattern for key change](/img/understanding/key-change.svg)
+
+### Key Change + Previous Data
+
+This pattern is to fetch data and change the key and revalidate it later with the `keepPreviousData` option.
+
+![A pattern for key change + previous data](/img/understanding/key-change-previous-data.svg)
+
+### Fallback
+
+This pattern is to fetch data and revalidate it later with fallback data.
+
+![A pattern for fallback](/img/understanding/fallback.svg)
+
+### Key Change + Fallback
+
+This pattern is to fetch data and change the key and revalidate it later with fallback data.
+
+![A pattern for key change + fallback](/img/understanding/key-change-fallback.svg)
+
+### Key Change + Previous Data + Fallback
+
+This pattern is to fetch data and change the key and revalidate it later with the `keepPreviousData` option and fallback data.
+
+![A pattern for key change + previous data + fallback](/img/understanding/key-change-previous-data-fallback.svg)
+
+## Combining with isLoading and isValidating for better UX
+
+Comparing to the existing `isValidating` value, `isLoading` is a new property that can help you for the more general loading cases for UX.
+
+- `isValidating` becomes `true` whenever there is an ongoing request **whatever the the data is loaded or not**
+- `isLoading` becomes `true` when there is an ongoing request and **data is not loaded yet**.
+
+Simply saying you can use `isValidating` for indicating everytime there is an ongoing revalidation, and `isLoading` for indicating that SWR is revalidating but there is no data yet to display.
+
+
+ Fallback data and previous data are not considered "loaded data," so when you use fallback data or enable the keepPreviousData option, you might have data to display.
+
+
+```jsx
+function Stock() {
+ const { data, isLoading, isValidating } = useSWR(STOCK_API, fetcher, {
+ refreshInterval: 3000
+ });
+
+ // If it's still loading the initial data, there is nothing to display.
+ // We return a skeleton here.
+ if (isLoading) return
;
+
+ // Otherwise, display the data and a spinner that indicates a background
+ // revalidation.
+ return (
+ <>
+ ${data}
+ {isValidating ?
: null}
+ >
+ );
+}
+```
+
+![An example of using the isLoading state](/img/understanding/isloading.gif)
+
+You can find the code example [here](https://codesandbox.io/s/swr-isloading-jtopow)
+
+## Return previous data for better UX
+
+When doing data fetching based on continuous user actions, e.g. real-time search when typing, keeping the previous fetched data can improve the UX a lot. `keepPreviousData` is an option to enable that behavior. Here's a simple search UI:
+
+```jsx
+function Search() {
+ const [search, setSearch] = React.useState('');
+
+ const { data, isLoading } = useSWR(`/search?q=${search}`, fetcher, {
+ keepPreviousData: true
+ });
+
+ return (
+
+
setSearch(e.target.value)}
+ placeholder="Search..."
+ />
+
+
+ {data?.products.map(item =>
)
+
+
+ );
+}
+```
+
+With `keepPreviousData` enabled, you will still get the previous data even if you change the SWR key and the data for the new key starts loading again.
+
+
+
+You can find the full code for this example here: https://codesandbox.io/s/swr-keeppreviousdata-fsjz3m.
+
+## Dependency Collection for performance
+
+SWR only triggers re-rendering when the states used in the component have been updated. If you only use `data` in the component, SWR ignores the updates of other properties like `isValidating`, and `isLoading`. This reduces rendering counts a lot. More information can be found [here](/docs/advanced/performance#dependency-collection).
diff --git a/pages/docs/advanced/understanding.es-ES.mdx b/pages/docs/advanced/understanding.es-ES.mdx
new file mode 100644
index 00000000..98e1c1b9
--- /dev/null
+++ b/pages/docs/advanced/understanding.es-ES.mdx
@@ -0,0 +1,125 @@
+import Callout from 'nextra-theme-docs/callout'
+import Video from 'components/video'
+
+# Understanding SWR
+
+## State Machine
+
+`useSWR` returns `data`, `error`, `isLoading`, and `isValidating` depending on the state of the `fetcher` function. This diagrams describe how SWR returns values in some scenarios.
+
+### Fetch and Revalidate
+
+This pattern is to fetch data and revalidate it later.
+
+![A pattern for fetch and revalidate](/img/understanding/fetch-and-revalidate.svg)
+
+### Key Change
+
+This pattern is to fetch data and change the key and revalidate it later.
+
+![A pattern for key change](/img/understanding/key-change.svg)
+
+### Key Change + Previous Data
+
+This pattern is to fetch data and change the key and revalidate it later with the `keepPreviousData` option.
+
+![A pattern for key change + previous data](/img/understanding/key-change-previous-data.svg)
+
+### Fallback
+
+This pattern is to fetch data and revalidate it later with fallback data.
+
+![A pattern for fallback](/img/understanding/fallback.svg)
+
+### Key Change + Fallback
+
+This pattern is to fetch data and change the key and revalidate it later with fallback data.
+
+![A pattern for key change + fallback](/img/understanding/key-change-fallback.svg)
+
+### Key Change + Previous Data + Fallback
+
+This pattern is to fetch data and change the key and revalidate it later with the `keepPreviousData` option and fallback data.
+
+![A pattern for key change + previous data + fallback](/img/understanding/key-change-previous-data-fallback.svg)
+
+## Combining with isLoading and isValidating for better UX
+
+Comparing to the existing `isValidating` value, `isLoading` is a new property that can help you for the more general loading cases for UX.
+
+- `isValidating` becomes `true` whenever there is an ongoing request **whatever the the data is loaded or not**
+- `isLoading` becomes `true` when there is an ongoing request and **data is not loaded yet**.
+
+Simply saying you can use `isValidating` for indicating everytime there is an ongoing revalidation, and `isLoading` for indicating that SWR is revalidating but there is no data yet to display.
+
+
+ Fallback data and previous data are not considered "loaded data," so when you use fallback data or enable the keepPreviousData option, you might have data to display.
+
+
+```jsx
+function Stock() {
+ const { data, isLoading, isValidating } = useSWR(STOCK_API, fetcher, {
+ refreshInterval: 3000
+ });
+
+ // If it's still loading the initial data, there is nothing to display.
+ // We return a skeleton here.
+ if (isLoading) return
;
+
+ // Otherwise, display the data and a spinner that indicates a background
+ // revalidation.
+ return (
+ <>
+ ${data}
+ {isValidating ?
: null}
+ >
+ );
+}
+```
+
+![An example of using the isLoading state](/img/understanding/isloading.gif)
+
+You can find the code example [here](https://codesandbox.io/s/swr-isloading-jtopow)
+
+## Return previous data for better UX
+
+When doing data fetching based on continuous user actions, e.g. real-time search when typing, keeping the previous fetched data can improve the UX a lot. `keepPreviousData` is an option to enable that behavior. Here's a simple search UI:
+
+```jsx
+function Search() {
+ const [search, setSearch] = React.useState('');
+
+ const { data, isLoading } = useSWR(`/search?q=${search}`, fetcher, {
+ keepPreviousData: true
+ });
+
+ return (
+
+
setSearch(e.target.value)}
+ placeholder="Search..."
+ />
+
+
+ {data?.products.map(item =>
)
+
+
+ );
+}
+```
+
+With `keepPreviousData` enabled, you will still get the previous data even if you change the SWR key and the data for the new key starts loading again.
+
+
+
+You can find the full code for this example here: https://codesandbox.io/s/swr-keeppreviousdata-fsjz3m.
+
+## Dependency Collection for performance
+
+SWR only triggers re-rendering when the states used in the component have been updated. If you only use `data` in the component, SWR ignores the updates of other properties like `isValidating`, and `isLoading`. This reduces rendering counts a lot. More information can be found [here](/docs/advanced/performance#dependency-collection).
diff --git a/pages/docs/advanced/understanding.ja.mdx b/pages/docs/advanced/understanding.ja.mdx
new file mode 100644
index 00000000..eaaf277d
--- /dev/null
+++ b/pages/docs/advanced/understanding.ja.mdx
@@ -0,0 +1,125 @@
+import Callout from 'nextra-theme-docs/callout'
+import Video from 'components/video'
+
+# SWR を理解する
+
+## 状態機械
+
+`useSWR` は `data`、`error`、`isLoading` と `isValidating` を `fetcher` 関数の状態に応じて返します。これらの図は SWR がそれぞれでどのように値を返すのかを示しています。
+
+### フェッチして再検証する
+
+これはフェッチして再検証するパターンです。
+
+![フェッチと再検証のパターン](/img/understanding/fetch-and-revalidate.svg)
+
+### キーの変更
+
+これはフェッチした後にキーを変更し、さらにその後再検証するパターンです。
+
+![キーが変更されたパターン](/img/understanding/key-change.svg)
+
+### キーの変更と以前のデータ
+
+これはフェッチした後にキーを変更し再検証するのを `keepPreviousData` オプション付きで行うパターンです。
+
+![キーの変更を keepPreviousData オプションありで行うパターン](/img/understanding/key-change-previous-data.svg)
+
+### フォールバック
+
+これはフェッチした後に再検証するのをフォールバックデータと一緒に行うパターンです。
+
+![フォールバックのパターン](/img/understanding/fallback.svg)
+
+### キーの変更とフォールバック
+
+これはフェッチした後にキーを変更し再検証するのをフォールバックデータと一緒に行うパターンです。
+
+![キーの変更をフォールバックありで行うパターン](/img/understanding/key-change-fallback.svg)
+
+### キーの変更と以前のデータとフォールバック
+
+これはフェッチした後にキーを変更し再検証するのを `keepPreviousData` オプションとフォールバックデータと一緒に行うパターンです。
+
+![キーの変更を keepPreviousData オプションとフォールバックありで行うパターン](/img/understanding/key-change-previous-data-fallback.svg)
+
+## よりよい UX のために isLoading と isValidating を組み合わせる
+
+既存の `isValidating` の値と比較して、`isLoading` はより一般的なローディングのための UX に役に立つ新しいプロパティです。
+
+- `isValidating` は実行中のリクエストがある場合に **データがロード済みかどうかに関わらず** `true` になります
+- `isLoading` は実行中のリクエストがあり ***ロード済みのデータがまだない場合** に `true` になります
+
+わかりやすく言うと、`isValidating` は実行中の再検証が存在する場合全てで、`isLoading` は SWR が再検証中だけど表示するためのデータがない場合に使えます。
+
+
+ フォールバックデータと以前のデータ (previous data) は "ロードされたデータ" とは扱われません。そのため、フォールバックデータや `keepPreviousData` オプションを使う場合には、表示するためのデータがあるかもしれません。
+
+
+```jsx
+function Stock() {
+ const { data, isLoading, isValidating } = useSWR(STOCK_API, fetcher, {
+ refreshInterval: 3000
+ });
+
+ // 初期データのローディング中の場合には、表示するためのデータはありません
+ // そのため、スケルトンの表示を返します
+ if (isLoading) return
;
+
+ // そうでなければ、データとバックグラウンドで再検証中であることを示す
+ // UI を表示します
+ return (
+ <>
+ ${data}
+ {isValidating ?
: null}
+ >
+ );
+}
+```
+
+![isLoading を使った例](/img/understanding/isloading.gif)
+
+[こちら](https://codesandbox.io/s/swr-isloading-jtopow) から実際のコードを確認できます。
+
+## よりよいユーザー体験のために以前のデータを返す
+
+キー入力に応じたリアルタイム検索など継続的なユーザーアクションをベースにしたデータ取得を行う時、以前のデータを維持することによりユーザー体験を改善できます。`keepPreviousData` はこれを可能にするオプションです。これはシンプルな検索の例です。
+
+```jsx
+function Search() {
+ const [search, setSearch] = React.useState('');
+
+ const { data, isLoading } = useSWR(`/search?q=${search}`, fetcher, {
+ keepPreviousData: true
+ });
+
+ return (
+
+
setSearch(e.target.value)}
+ placeholder="Search..."
+ />
+
+
+ {data?.products.map(item =>
)
+
+
+ );
+}
+```
+
+`keepPreviousData` が有効にされている場合、SWR のキーが変わり新しいデータの取得が開始された場合にも以前のキーに対応するデータが返されます。
+
+
+
+この例のコードはこちらです: https://codesandbox.io/s/swr-keeppreviousdata-fsjz3m.
+
+## パフォーマンのための依存収集
+
+SWR はコンポーネントで使われているデータが更新された場合のみ再レンダリングします。コンポーネントの中で `data` しか使っていない場合、SWR は `isValidating` や `isLoading` のプロパティの更新を無視します。これはレンダリングの回数を減らします。詳細については [こちら](/docs/advanced/performance#dependency-collection)。
diff --git a/pages/docs/advanced/understanding.ko.mdx b/pages/docs/advanced/understanding.ko.mdx
new file mode 100644
index 00000000..98e1c1b9
--- /dev/null
+++ b/pages/docs/advanced/understanding.ko.mdx
@@ -0,0 +1,125 @@
+import Callout from 'nextra-theme-docs/callout'
+import Video from 'components/video'
+
+# Understanding SWR
+
+## State Machine
+
+`useSWR` returns `data`, `error`, `isLoading`, and `isValidating` depending on the state of the `fetcher` function. This diagrams describe how SWR returns values in some scenarios.
+
+### Fetch and Revalidate
+
+This pattern is to fetch data and revalidate it later.
+
+![A pattern for fetch and revalidate](/img/understanding/fetch-and-revalidate.svg)
+
+### Key Change
+
+This pattern is to fetch data and change the key and revalidate it later.
+
+![A pattern for key change](/img/understanding/key-change.svg)
+
+### Key Change + Previous Data
+
+This pattern is to fetch data and change the key and revalidate it later with the `keepPreviousData` option.
+
+![A pattern for key change + previous data](/img/understanding/key-change-previous-data.svg)
+
+### Fallback
+
+This pattern is to fetch data and revalidate it later with fallback data.
+
+![A pattern for fallback](/img/understanding/fallback.svg)
+
+### Key Change + Fallback
+
+This pattern is to fetch data and change the key and revalidate it later with fallback data.
+
+![A pattern for key change + fallback](/img/understanding/key-change-fallback.svg)
+
+### Key Change + Previous Data + Fallback
+
+This pattern is to fetch data and change the key and revalidate it later with the `keepPreviousData` option and fallback data.
+
+![A pattern for key change + previous data + fallback](/img/understanding/key-change-previous-data-fallback.svg)
+
+## Combining with isLoading and isValidating for better UX
+
+Comparing to the existing `isValidating` value, `isLoading` is a new property that can help you for the more general loading cases for UX.
+
+- `isValidating` becomes `true` whenever there is an ongoing request **whatever the the data is loaded or not**
+- `isLoading` becomes `true` when there is an ongoing request and **data is not loaded yet**.
+
+Simply saying you can use `isValidating` for indicating everytime there is an ongoing revalidation, and `isLoading` for indicating that SWR is revalidating but there is no data yet to display.
+
+
+ Fallback data and previous data are not considered "loaded data," so when you use fallback data or enable the keepPreviousData option, you might have data to display.
+
+
+```jsx
+function Stock() {
+ const { data, isLoading, isValidating } = useSWR(STOCK_API, fetcher, {
+ refreshInterval: 3000
+ });
+
+ // If it's still loading the initial data, there is nothing to display.
+ // We return a skeleton here.
+ if (isLoading) return
;
+
+ // Otherwise, display the data and a spinner that indicates a background
+ // revalidation.
+ return (
+ <>
+ ${data}
+ {isValidating ?
: null}
+ >
+ );
+}
+```
+
+![An example of using the isLoading state](/img/understanding/isloading.gif)
+
+You can find the code example [here](https://codesandbox.io/s/swr-isloading-jtopow)
+
+## Return previous data for better UX
+
+When doing data fetching based on continuous user actions, e.g. real-time search when typing, keeping the previous fetched data can improve the UX a lot. `keepPreviousData` is an option to enable that behavior. Here's a simple search UI:
+
+```jsx
+function Search() {
+ const [search, setSearch] = React.useState('');
+
+ const { data, isLoading } = useSWR(`/search?q=${search}`, fetcher, {
+ keepPreviousData: true
+ });
+
+ return (
+
+
setSearch(e.target.value)}
+ placeholder="Search..."
+ />
+
+
+ {data?.products.map(item =>
)
+
+
+ );
+}
+```
+
+With `keepPreviousData` enabled, you will still get the previous data even if you change the SWR key and the data for the new key starts loading again.
+
+
+
+You can find the full code for this example here: https://codesandbox.io/s/swr-keeppreviousdata-fsjz3m.
+
+## Dependency Collection for performance
+
+SWR only triggers re-rendering when the states used in the component have been updated. If you only use `data` in the component, SWR ignores the updates of other properties like `isValidating`, and `isLoading`. This reduces rendering counts a lot. More information can be found [here](/docs/advanced/performance#dependency-collection).
diff --git a/pages/docs/advanced/understanding.pt-BR.mdx b/pages/docs/advanced/understanding.pt-BR.mdx
new file mode 100644
index 00000000..98e1c1b9
--- /dev/null
+++ b/pages/docs/advanced/understanding.pt-BR.mdx
@@ -0,0 +1,125 @@
+import Callout from 'nextra-theme-docs/callout'
+import Video from 'components/video'
+
+# Understanding SWR
+
+## State Machine
+
+`useSWR` returns `data`, `error`, `isLoading`, and `isValidating` depending on the state of the `fetcher` function. This diagrams describe how SWR returns values in some scenarios.
+
+### Fetch and Revalidate
+
+This pattern is to fetch data and revalidate it later.
+
+![A pattern for fetch and revalidate](/img/understanding/fetch-and-revalidate.svg)
+
+### Key Change
+
+This pattern is to fetch data and change the key and revalidate it later.
+
+![A pattern for key change](/img/understanding/key-change.svg)
+
+### Key Change + Previous Data
+
+This pattern is to fetch data and change the key and revalidate it later with the `keepPreviousData` option.
+
+![A pattern for key change + previous data](/img/understanding/key-change-previous-data.svg)
+
+### Fallback
+
+This pattern is to fetch data and revalidate it later with fallback data.
+
+![A pattern for fallback](/img/understanding/fallback.svg)
+
+### Key Change + Fallback
+
+This pattern is to fetch data and change the key and revalidate it later with fallback data.
+
+![A pattern for key change + fallback](/img/understanding/key-change-fallback.svg)
+
+### Key Change + Previous Data + Fallback
+
+This pattern is to fetch data and change the key and revalidate it later with the `keepPreviousData` option and fallback data.
+
+![A pattern for key change + previous data + fallback](/img/understanding/key-change-previous-data-fallback.svg)
+
+## Combining with isLoading and isValidating for better UX
+
+Comparing to the existing `isValidating` value, `isLoading` is a new property that can help you for the more general loading cases for UX.
+
+- `isValidating` becomes `true` whenever there is an ongoing request **whatever the the data is loaded or not**
+- `isLoading` becomes `true` when there is an ongoing request and **data is not loaded yet**.
+
+Simply saying you can use `isValidating` for indicating everytime there is an ongoing revalidation, and `isLoading` for indicating that SWR is revalidating but there is no data yet to display.
+
+
+ Fallback data and previous data are not considered "loaded data," so when you use fallback data or enable the keepPreviousData option, you might have data to display.
+
+
+```jsx
+function Stock() {
+ const { data, isLoading, isValidating } = useSWR(STOCK_API, fetcher, {
+ refreshInterval: 3000
+ });
+
+ // If it's still loading the initial data, there is nothing to display.
+ // We return a skeleton here.
+ if (isLoading) return
;
+
+ // Otherwise, display the data and a spinner that indicates a background
+ // revalidation.
+ return (
+ <>
+ ${data}
+ {isValidating ?
: null}
+ >
+ );
+}
+```
+
+![An example of using the isLoading state](/img/understanding/isloading.gif)
+
+You can find the code example [here](https://codesandbox.io/s/swr-isloading-jtopow)
+
+## Return previous data for better UX
+
+When doing data fetching based on continuous user actions, e.g. real-time search when typing, keeping the previous fetched data can improve the UX a lot. `keepPreviousData` is an option to enable that behavior. Here's a simple search UI:
+
+```jsx
+function Search() {
+ const [search, setSearch] = React.useState('');
+
+ const { data, isLoading } = useSWR(`/search?q=${search}`, fetcher, {
+ keepPreviousData: true
+ });
+
+ return (
+
+
setSearch(e.target.value)}
+ placeholder="Search..."
+ />
+
+
+ {data?.products.map(item =>
)
+
+
+ );
+}
+```
+
+With `keepPreviousData` enabled, you will still get the previous data even if you change the SWR key and the data for the new key starts loading again.
+
+
+
+You can find the full code for this example here: https://codesandbox.io/s/swr-keeppreviousdata-fsjz3m.
+
+## Dependency Collection for performance
+
+SWR only triggers re-rendering when the states used in the component have been updated. If you only use `data` in the component, SWR ignores the updates of other properties like `isValidating`, and `isLoading`. This reduces rendering counts a lot. More information can be found [here](/docs/advanced/performance#dependency-collection).
diff --git a/pages/docs/advanced/understanding.ru.mdx b/pages/docs/advanced/understanding.ru.mdx
new file mode 100644
index 00000000..98e1c1b9
--- /dev/null
+++ b/pages/docs/advanced/understanding.ru.mdx
@@ -0,0 +1,125 @@
+import Callout from 'nextra-theme-docs/callout'
+import Video from 'components/video'
+
+# Understanding SWR
+
+## State Machine
+
+`useSWR` returns `data`, `error`, `isLoading`, and `isValidating` depending on the state of the `fetcher` function. This diagrams describe how SWR returns values in some scenarios.
+
+### Fetch and Revalidate
+
+This pattern is to fetch data and revalidate it later.
+
+![A pattern for fetch and revalidate](/img/understanding/fetch-and-revalidate.svg)
+
+### Key Change
+
+This pattern is to fetch data and change the key and revalidate it later.
+
+![A pattern for key change](/img/understanding/key-change.svg)
+
+### Key Change + Previous Data
+
+This pattern is to fetch data and change the key and revalidate it later with the `keepPreviousData` option.
+
+![A pattern for key change + previous data](/img/understanding/key-change-previous-data.svg)
+
+### Fallback
+
+This pattern is to fetch data and revalidate it later with fallback data.
+
+![A pattern for fallback](/img/understanding/fallback.svg)
+
+### Key Change + Fallback
+
+This pattern is to fetch data and change the key and revalidate it later with fallback data.
+
+![A pattern for key change + fallback](/img/understanding/key-change-fallback.svg)
+
+### Key Change + Previous Data + Fallback
+
+This pattern is to fetch data and change the key and revalidate it later with the `keepPreviousData` option and fallback data.
+
+![A pattern for key change + previous data + fallback](/img/understanding/key-change-previous-data-fallback.svg)
+
+## Combining with isLoading and isValidating for better UX
+
+Comparing to the existing `isValidating` value, `isLoading` is a new property that can help you for the more general loading cases for UX.
+
+- `isValidating` becomes `true` whenever there is an ongoing request **whatever the the data is loaded or not**
+- `isLoading` becomes `true` when there is an ongoing request and **data is not loaded yet**.
+
+Simply saying you can use `isValidating` for indicating everytime there is an ongoing revalidation, and `isLoading` for indicating that SWR is revalidating but there is no data yet to display.
+
+
+ Fallback data and previous data are not considered "loaded data," so when you use fallback data or enable the keepPreviousData option, you might have data to display.
+
+
+```jsx
+function Stock() {
+ const { data, isLoading, isValidating } = useSWR(STOCK_API, fetcher, {
+ refreshInterval: 3000
+ });
+
+ // If it's still loading the initial data, there is nothing to display.
+ // We return a skeleton here.
+ if (isLoading) return
;
+
+ // Otherwise, display the data and a spinner that indicates a background
+ // revalidation.
+ return (
+ <>
+ ${data}
+ {isValidating ?
: null}
+ >
+ );
+}
+```
+
+![An example of using the isLoading state](/img/understanding/isloading.gif)
+
+You can find the code example [here](https://codesandbox.io/s/swr-isloading-jtopow)
+
+## Return previous data for better UX
+
+When doing data fetching based on continuous user actions, e.g. real-time search when typing, keeping the previous fetched data can improve the UX a lot. `keepPreviousData` is an option to enable that behavior. Here's a simple search UI:
+
+```jsx
+function Search() {
+ const [search, setSearch] = React.useState('');
+
+ const { data, isLoading } = useSWR(`/search?q=${search}`, fetcher, {
+ keepPreviousData: true
+ });
+
+ return (
+
+
setSearch(e.target.value)}
+ placeholder="Search..."
+ />
+
+
+ {data?.products.map(item =>
)
+
+
+ );
+}
+```
+
+With `keepPreviousData` enabled, you will still get the previous data even if you change the SWR key and the data for the new key starts loading again.
+
+
+
+You can find the full code for this example here: https://codesandbox.io/s/swr-keeppreviousdata-fsjz3m.
+
+## Dependency Collection for performance
+
+SWR only triggers re-rendering when the states used in the component have been updated. If you only use `data` in the component, SWR ignores the updates of other properties like `isValidating`, and `isLoading`. This reduces rendering counts a lot. More information can be found [here](/docs/advanced/performance#dependency-collection).
diff --git a/pages/docs/advanced/understanding.zh-CN.mdx b/pages/docs/advanced/understanding.zh-CN.mdx
new file mode 100644
index 00000000..98e1c1b9
--- /dev/null
+++ b/pages/docs/advanced/understanding.zh-CN.mdx
@@ -0,0 +1,125 @@
+import Callout from 'nextra-theme-docs/callout'
+import Video from 'components/video'
+
+# Understanding SWR
+
+## State Machine
+
+`useSWR` returns `data`, `error`, `isLoading`, and `isValidating` depending on the state of the `fetcher` function. This diagrams describe how SWR returns values in some scenarios.
+
+### Fetch and Revalidate
+
+This pattern is to fetch data and revalidate it later.
+
+![A pattern for fetch and revalidate](/img/understanding/fetch-and-revalidate.svg)
+
+### Key Change
+
+This pattern is to fetch data and change the key and revalidate it later.
+
+![A pattern for key change](/img/understanding/key-change.svg)
+
+### Key Change + Previous Data
+
+This pattern is to fetch data and change the key and revalidate it later with the `keepPreviousData` option.
+
+![A pattern for key change + previous data](/img/understanding/key-change-previous-data.svg)
+
+### Fallback
+
+This pattern is to fetch data and revalidate it later with fallback data.
+
+![A pattern for fallback](/img/understanding/fallback.svg)
+
+### Key Change + Fallback
+
+This pattern is to fetch data and change the key and revalidate it later with fallback data.
+
+![A pattern for key change + fallback](/img/understanding/key-change-fallback.svg)
+
+### Key Change + Previous Data + Fallback
+
+This pattern is to fetch data and change the key and revalidate it later with the `keepPreviousData` option and fallback data.
+
+![A pattern for key change + previous data + fallback](/img/understanding/key-change-previous-data-fallback.svg)
+
+## Combining with isLoading and isValidating for better UX
+
+Comparing to the existing `isValidating` value, `isLoading` is a new property that can help you for the more general loading cases for UX.
+
+- `isValidating` becomes `true` whenever there is an ongoing request **whatever the the data is loaded or not**
+- `isLoading` becomes `true` when there is an ongoing request and **data is not loaded yet**.
+
+Simply saying you can use `isValidating` for indicating everytime there is an ongoing revalidation, and `isLoading` for indicating that SWR is revalidating but there is no data yet to display.
+
+
+ Fallback data and previous data are not considered "loaded data," so when you use fallback data or enable the keepPreviousData option, you might have data to display.
+
+
+```jsx
+function Stock() {
+ const { data, isLoading, isValidating } = useSWR(STOCK_API, fetcher, {
+ refreshInterval: 3000
+ });
+
+ // If it's still loading the initial data, there is nothing to display.
+ // We return a skeleton here.
+ if (isLoading) return
;
+
+ // Otherwise, display the data and a spinner that indicates a background
+ // revalidation.
+ return (
+ <>
+ ${data}
+ {isValidating ?
: null}
+ >
+ );
+}
+```
+
+![An example of using the isLoading state](/img/understanding/isloading.gif)
+
+You can find the code example [here](https://codesandbox.io/s/swr-isloading-jtopow)
+
+## Return previous data for better UX
+
+When doing data fetching based on continuous user actions, e.g. real-time search when typing, keeping the previous fetched data can improve the UX a lot. `keepPreviousData` is an option to enable that behavior. Here's a simple search UI:
+
+```jsx
+function Search() {
+ const [search, setSearch] = React.useState('');
+
+ const { data, isLoading } = useSWR(`/search?q=${search}`, fetcher, {
+ keepPreviousData: true
+ });
+
+ return (
+
+
setSearch(e.target.value)}
+ placeholder="Search..."
+ />
+
+
+ {data?.products.map(item =>
)
+
+
+ );
+}
+```
+
+With `keepPreviousData` enabled, you will still get the previous data even if you change the SWR key and the data for the new key starts loading again.
+
+
+
+You can find the full code for this example here: https://codesandbox.io/s/swr-keeppreviousdata-fsjz3m.
+
+## Dependency Collection for performance
+
+SWR only triggers re-rendering when the states used in the component have been updated. If you only use `data` in the component, SWR ignores the updates of other properties like `isValidating`, and `isLoading`. This reduces rendering counts a lot. More information can be found [here](/docs/advanced/performance#dependency-collection).
diff --git a/pages/docs/options.en-US.mdx b/pages/docs/api.en-US.mdx
similarity index 84%
rename from pages/docs/options.en-US.mdx
rename to pages/docs/api.en-US.mdx
index 4112cc59..5a19c092 100644
--- a/pages/docs/options.en-US.mdx
+++ b/pages/docs/api.en-US.mdx
@@ -1,14 +1,15 @@
import Callout from 'nextra-theme-docs/callout'
-# API Options
+# API
```js
-const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
+const { data, error, isLoading, isValidating, mutate } = useSWR(key, fetcher, options)
```
+
## Parameters
-- `key`: a unique key string for the request (or a function / array / null) [(advanced usage)](/docs/conditional-fetching)
+- `key`: a unique key string for the request (or a function / array / null) [(details)](/docs/arguments), [(advanced usage)](/docs/conditional-fetching)
- `fetcher`: (_optional_) a Promise returning function to fetch your data [(details)](/docs/data-fetching)
- `options`: (_optional_) an object of options for this SWR hook
@@ -16,9 +17,12 @@ const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
- `data`: data for the given key resolved by `fetcher` (or undefined if not loaded)
- `error`: error thrown by `fetcher` (or undefined)
+- `isLoading`: if there's an ongoing request and no "loaded data". Fallback data and previous data are not considered "loaded data"
- `isValidating`: if there's a request or revalidation loading
- `mutate(data?, options?)`: function to mutate the cached data [(details)](/docs/mutation)
+More information can be found [here](/docs/advanced/understanding).
+
## Options
- `suspense = false`: enable React Suspense mode [(details)](/docs/suspense)
@@ -41,10 +45,12 @@ const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
- `errorRetryCount`: max error retry count
- `fallback`: a key-value object of multiple fallback data [(example)](/docs/with-nextjs)
- `fallbackData`: initial data to be returned (note: This is per-hook)
+- `keepPreviousData = false`: return the previous key's data until the new data has been loaded [(details)](/docs/advanced/understanding#return-previous-data-for-better-ux)
- `onLoadingSlow(key, config)`: callback function when a request takes too long to load (see `loadingTimeout`)
- `onSuccess(data, key, config)`: callback function when a request finishes successfully
- `onError(err, key, config)`: callback function when a request returns an error
- `onErrorRetry(err, key, config, revalidate, revalidateOps)`: handler for error retry
+- `onDiscarded(key)`: callback function when a request is ignored due to race conditions
- `compare(a, b)`: comparison function used to detect when returned data has changed, to avoid spurious rerenders. By default, [stable-hash](https://github.com/shuding/stable-hash) is used.
- `isPaused()`: function to detect whether pause revalidations, will ignore fetched data and errors when it returns `true`. Returns `false` by default.
- `use`: array of middleware functions [(details)](/docs/middleware)
diff --git a/pages/docs/options.es-ES.mdx b/pages/docs/api.es-ES.mdx
similarity index 84%
rename from pages/docs/options.es-ES.mdx
rename to pages/docs/api.es-ES.mdx
index 76d43196..8f1bb88e 100644
--- a/pages/docs/options.es-ES.mdx
+++ b/pages/docs/api.es-ES.mdx
@@ -1,14 +1,14 @@
import Callout from 'nextra-theme-docs/callout'
-# API Options
+# API
```js
-const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
+const { data, error, isLoading, isValidating, mutate } = useSWR(key, fetcher, options)
```
## Parámetros
-- `key`: un string key único para la solicitud (o una función / array / null) [(uso avanzado)](/docs/conditional-fetching)
+- `key`: un string key único para la solicitud (o una función / array / null) [(detalles)](/docs/arguments), [(uso avanzado)](/docs/conditional-fetching)
- `fetcher`: (_opcional_) una función que devuelve una Promise para recuperar sus datos [(detalles)](/docs/data-fetching)
- `options`: (_opcional_) un objecto de opciónes para el hook useSWR
@@ -16,9 +16,12 @@ const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
- `data`: data de la key dada resueltos por el `fetcher` (o undefined si no está cargado)
- `error`: error lanzado por el `fetcher` (o undefined)
+- `isLoading`: if there's an ongoing request and no "loaded data". Fallback data and previous data are not considered "loaded data"
- `isValidating`: si hay una solicitud o carga de revalidación
- `mutate(data?, options?`: función para mutar los datos en caché [(detalles)](/docs/mutation)
+More information can be found [here](/docs/advanced/understanding).
+
## Opciones
- `suspense = false`: activar el modo React Suspense [(detalles)](/docs/suspense)
@@ -42,10 +45,12 @@ const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
- `errorRetryCount`: número máximo de reintentos de error
- `fallback`: a key-value object of multiple fallback data [(example)](/docs/with-nextjs)
- `fallbackData`: datos iniciales a devolver (nota: Esto es por un hook)
+- `keepPreviousData = false`: return the previous key's data until the new data has been loaded [(detalles)](/docs/advanced/understanding#return-previous-data-for-better-ux)
- `onLoadingSlow(key, config)`: función callback cuando una petición tarda demasiado en cargase (véase: `loadingTimeout`)
- `onSuccess(data, key, config)`: función callback cuando una petición termina con éxito
- `onError(err, key, config)`: función callback cuando una petición devuelve un error
- `onErrorRetry(err, key, config, revalidate, revalidateOps)`: manejador para el reintento de error
+- `onDiscarded(key)`: callback function when a request is ignored due to race conditions
- `compare(a, b)`: función de comparación utilizada para detectar cuando los datos devueltos han cambiado,
para evitar spurious rerenders. Por defecto, se utiliza [stable-hash](https://github.com/shuding/stable-hash).
- `isPaused()`: para detectar si pause revalidations, ignorará los datos obtenidos y los errores cuando devuelva `true`. Devuelve `false` por defecto.
diff --git a/pages/docs/options.ja.mdx b/pages/docs/api.ja.mdx
similarity index 83%
rename from pages/docs/options.ja.mdx
rename to pages/docs/api.ja.mdx
index f21ce3e4..69498da9 100644
--- a/pages/docs/options.ja.mdx
+++ b/pages/docs/api.ja.mdx
@@ -1,14 +1,14 @@
import Callout from 'nextra-theme-docs/callout'
-# API オプション
+# API
```js
-const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
+const { data, error, isLoading, isValidating, mutate } = useSWR(key, fetcher, options)
```
## パラメーター
-- `key`: このリクエストのためのユニークなキー文字列(または関数、配列、null) [(高度な使用法)](/docs/conditional-fetching)
+- `key`: このリクエストのためのユニークなキー文字列(または関数、配列、null) [(詳細)](/docs/arguments), [(高度な使用法)](/docs/conditional-fetching)
- `fetcher`: (_任意_) データをフェッチするための Promise を返す関数 [( 詳細 )](/docs/data-fetching)
- `options`: (_任意_) この SWR フックのオプションオブジェクト
@@ -16,9 +16,12 @@ const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
- `data`: `fetcher` によって解決された、指定されたキーのデータ(もしくは、ロードされていない場合は undefined)
- `error`: `fetcher` によって投げられたエラー (もしくは undefined)
+- `isLoading`: 実行中のリクエストがあり "ロードされたデータ" がない状態。フォールバックのためのデータや `keepPreviousData` による以前のデータは "ロードされたデータ" ではありません
- `isValidating`: リクエストまたは再検証の読み込みがある場合
- `mutate(data?, options?)`: キャッシュされたデータを更新する関数 [( 詳細 )](/docs/mutation)
+More information can be found [here](/docs/advanced/understanding).
+
## オプション
- `suspense = false`: React Suspense モードを有効にする [( 詳細 )](/docs/suspense)
@@ -41,10 +44,12 @@ const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
- `errorRetryCount`: 最大エラー再試行回数
- `fallback`: 初期データの key-value オブジェクト [( 例 )](/docs/with-nextjs)
- `fallbackData`: 返される初期データ(注:フックごとに)
+- `keepPreviousData = false`: 新しいキーに対するデータがロードされるまで以前のキーのデータを返すかどうか [(詳細)](/docs/advanced/understanding#return-previous-data-for-better-ux)
- `onLoadingSlow(key, config)`: リクエストの読み込みに時間がかかりすぎる場合のコールバック関数(`loadingTimeout` を参照してください)
- `onSuccess(data, key, config)`: リクエストが正常に終了したときのコールバック関数
- `onError(err, key, config)`: リクエストがエラーを返したときのコールバック関数
- `onErrorRetry(err, key, config, revalidate, revalidateOps)`: エラー再試行のハンドラー
+- `onDiscarded(key)`: レースコンディションによりリクエストが無視されたときのコールバック関数
- `compare(a, b)`: 誤った再レンダリングを回避するために、返されたデータがいつ変更されたかを検出するために使用される比較関数。デフォルトでは、[stable-hash](https://github.com/shuding/stable-hash) が使用されます
- `isPaused()`: 再検証を一時停止するかどうかを検出する関数であり、この関数が `true` を返す場合はフェッチしたデータとエラーを無視します。デフォルトでは `false` を返します
- `use`: ミドルウェア関数の配列 [( 詳細 )](/docs/middleware)
diff --git a/pages/docs/options.ko.mdx b/pages/docs/api.ko.mdx
similarity index 84%
rename from pages/docs/options.ko.mdx
rename to pages/docs/api.ko.mdx
index ebb841f9..24b1f55c 100644
--- a/pages/docs/options.ko.mdx
+++ b/pages/docs/api.ko.mdx
@@ -1,14 +1,14 @@
import Callout from 'nextra-theme-docs/callout'
-# API 옵션
+# API
```js
-const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
+const { data, error, isLoading, isValidating, mutate } = useSWR(key, fetcher, options)
```
## 파라미터
-- `key`: 요청을 위한 고유한 키 문자열(또는 함수 / 배열 / null) [(고급 사용법)](/docs/conditional-fetching)
+- `key`: 요청을 위한 고유한 키 문자열(또는 함수 / 배열 / null) [(상세내용)](/docs/arguments), [(고급 사용법)](/docs/conditional-fetching)
- `fetcher`: (_옵션_) 데이터를 가져오기 위한 함수를 반환하는 Promise [(상세내용)](/docs/data-fetching)
- `options`: (_옵션_) SWR hook을 위한 옵션 객체
@@ -16,9 +16,12 @@ const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
- `data`: `fetcher`가 이행한 주어진 키에 대한 데이터(로드되지 않았다면 undefined)
- `error`: `fetcher`가 던진 에러(또는 undefined)
+- `isLoading`: if there's an ongoing request and no "loaded data". Fallback data and previous data are not considered "loaded data"
- `isValidating`: 요청이나 갱신 로딩의 여부
- `mutate(data?, options?)`: 캐시 된 데이터를 뮤테이트하기 위한 함수 [(상세내용)](/docs/mutation)
+More information can be found [here](/docs/advanced/understanding).
+
## 옵션
- `suspense = false`: React Suspense 모드를 활성화 [(상세내용)](/docs/suspense)
@@ -41,10 +44,12 @@ const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
- `errorRetryCount`: 최대 에러 재시도 수
- `fallback`: 다중 폴백 데이터의 키-값 객체 [(예시)](/docs/with-nextjs)
- `fallbackData`: 반환될 초기 데이터(노트: hook 별로 존재)
+- `keepPreviousData = false`: return the previous key's data until the new data has been loaded [(상세내용)](/docs/advanced/understanding#return-previous-data-for-better-ux)
- `onLoadingSlow(key, config)`: 요청을 로드하는 데 너무 오래 걸리는 경우의 콜백 함수(`loadingTimeout`을 보세요)
- `onSuccess(data, key, config)`: 요청이 성공적으로 종료되었을 경우의 콜백 함수
- `onError(err, key, config)`: 요청이 에러를 반환했을 경우의 콜백 함수
- `onErrorRetry(err, key, config, revalidate, revalidateOps)`: 에러 재시도 핸들러
+- `onDiscarded(key)`: callback function when a request is ignored due to race conditions
- `compare(a, b)`: 비논리적인 리렌더러를 회피하기 위해 반환된 데이터가 변경되었는지를 감지하는데 사용하는 비교 함수. 기본적으로 [stable-hash](https://github.com/shuding/stable-hash)을 사용합니다.
- `isPaused()`: 갱신의 중지 여부를 감지하는 함수. `true`가 반환될 경우 가져온 데이터와 에러는 무시합니다. 기본적으로는 `false`를 반환합니다.
- `use`: 미들웨어 함수의 배열 [(상세내용)](/docs/middleware)
diff --git a/pages/docs/options.pt-BR.mdx b/pages/docs/api.pt-BR.mdx
similarity index 84%
rename from pages/docs/options.pt-BR.mdx
rename to pages/docs/api.pt-BR.mdx
index cba2f4da..a86096d6 100644
--- a/pages/docs/options.pt-BR.mdx
+++ b/pages/docs/api.pt-BR.mdx
@@ -1,14 +1,14 @@
import Callout from 'nextra-theme-docs/callout'
-# Opções da API
+# API
```js
-const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
+const { data, error, isLoading, isValidating, mutate } = useSWR(key, fetcher, options)
```
## Parâmetros
-- `key`: uma chave única em string para o pedido (ou uma função / array / null) [(uso avançado)](/docs/conditional-fetching)
+- `key`: uma chave única em string para o pedido (ou uma função / array / null) [(detalhes)](/docs/arguments), [(uso avançado)](/docs/conditional-fetching)
- `fetcher`: (_opcional_) uma Promise retornando uma função para obter seus dados [(detalhes)](/docs/data-fetching)
- `options`: (_opcional_) um objeto de opções para esse hook SWR
@@ -16,9 +16,12 @@ const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
- `data`: dados para a chave fornecida pelo `fetcher` (ou undefined se não carregado)
- `error`: erro lançado pelo `fetcher` (ou undefined)
+- `isLoading`: if there's an ongoing request and no "loaded data". Fallback data and previous data are not considered "loaded data"
- `isValidating`: se há um pedido ou revalidação carregando
- `mutate(data?, options?)`: função para mutar os dados em cache [(detalhes)](/docs/mutation)
+More information can be found [here](/docs/advanced/understanding).
+
## Opções
- `suspense = false`: habilita o modo Suspense do React [(detalhes)](/docs/suspense)
@@ -41,10 +44,12 @@ const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
- `errorRetryCount`: número máximo de tentativas de erro
- `fallback`: um objeto de chave-valor de vários dados de fallback [(exemplo)](/docs/with-nextjs)
- `fallbackData`: dados iniciais para ser retornado (nota: isto é por-hook)
+- `keepPreviousData = false`: return the previous key's data until the new data has been loaded [(detalhes)](/docs/advanced/understanding#return-previous-data-for-better-ux)
- `onLoadingSlow(key, config)`: função callback quando um pedido demora muito tempo a carregar (veja `loadingTimeout`)
- `onSuccess(data, key, config)`: função callback quando um pedido é bem-sucedido
- `onError(err, key, config)`: função callback quando um pedido retorna um erro
- `onErrorRetry(err, key, config, revalidate, revalidateOps)`: função callback quando um pedido retorna um erro
+- `onDiscarded(key)`: callback function when a request is ignored due to race conditions
- `compare(a, b)`: função de comparação para detectar quando o dado retornado está diferente, para evitar rerenderizações desnecessárias. Por padrão, [stable-hash](https://github.com/shuding/stable-hash) é usado.
- `isPaused()`: função para detectar quando o revalidador está pausado, e deve ignorar os dados e erros quando retorna `true`. Por padrão, retorna `false`.
- `use`: array de middleware functions [(details)](/docs/middleware)
diff --git a/pages/docs/options.ru.mdx b/pages/docs/api.ru.mdx
similarity index 88%
rename from pages/docs/options.ru.mdx
rename to pages/docs/api.ru.mdx
index 4ebe7155..fa1aef23 100644
--- a/pages/docs/options.ru.mdx
+++ b/pages/docs/api.ru.mdx
@@ -1,14 +1,14 @@
import Callout from 'nextra-theme-docs/callout'
-# Опции API
+# API
```js
-const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
+const { data, error, isLoading, isValidating, mutate } = useSWR(key, fetcher, options)
```
## Параметры
-- `key`: уникальный строчный ключ для запроса (или функция / массив / null) [(продвинутое использование)](/docs/conditional-fetching)
+- `key`: уникальный строчный ключ для запроса (или функция / массив / null) [(подробнее)](/docs/arguments), [(продвинутое использование)](/docs/conditional-fetching)
- `fetcher`: (_опционально_) Promise возвращающий функцию для выборки данных [(подробнее)](/docs/data-fetching)
- `options`: (_опционально_) объект опций для этого SWR хука
@@ -16,9 +16,12 @@ const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
- `data`: данные для данного ключа, разрешенные `fetcher` (или undefined, если не загружено)
- `error`: ошибка, выброшенная `fetcher`-ом (или undefined)
+- `isLoading`: if there's an ongoing request and no "loaded data". Fallback data and previous data are not considered "loaded data"
- `isValidating`: если запрос или ревалидация загружается
- `mutate(data?, options?)`: функция для мутации закешированных данных [(подробнее)](/docs/mutation)
+More information can be found [here](/docs/advanced/understanding).
+
## Опции
- `suspense = false`: включить режим задержки React Suspense [(подробнее)](/docs/suspense)
@@ -41,10 +44,12 @@ const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
- `errorRetryCount`: максимальное количество повторных попыток при ошибке
- `fallback`: объект типа "ключ-значение" из нескольких резервных данных [(example)](/docs/with-nextjs)
- `fallbackData`: исходные данные, которые должны быть возвращены (примечание: это для каждого хука)
+- `keepPreviousData = false`: return the previous key's data until the new data has been loaded [(подробнее)](/docs/advanced/understanding#return-previous-data-for-better-ux)
- `onLoadingSlow(key, config)`: колбэк-функция, когда запрос загружается слишком долго (см. `loadingTimeout`)
- `onSuccess(data, key, config)`: колбэк-функция при успешном завершении запроса
- `onError(err, key, config)`: колбэк-функция, когда запрос возвращает ошибку
- `onErrorRetry(err, key, config, revalidate, revalidateOps)`: обработчик повторной попытки при ошибке
+- `onDiscarded(key)`: callback function when a request is ignored due to race conditions
- `compare(a, b)`: функция сравнения, используемая для определения того, когда возвращаемые данные были изменены, чтобы избежать ложных повторных отрисовок. По умолчанию используется [stable-hash](https://github.com/shuding/stable-hash).
- `isPaused()`: функция, определяющая, приостанавливается ли ревалидация, игнорирует полученные данные и ошибки, когда возвращает `true`. По умолчанию возвращает `false`.
- `use`: массив middleware-функций [(подробнее)](/docs/middleware)
diff --git a/pages/docs/options.zh-CN.mdx b/pages/docs/api.zh-CN.mdx
similarity index 83%
rename from pages/docs/options.zh-CN.mdx
rename to pages/docs/api.zh-CN.mdx
index 3e2b88aa..e55e8019 100644
--- a/pages/docs/options.zh-CN.mdx
+++ b/pages/docs/api.zh-CN.mdx
@@ -1,14 +1,14 @@
import Callout from 'nextra-theme-docs/callout'
-# API 选项
+# API
```js
-const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
+const { data, error, isLoading, isValidating, mutate } = useSWR(key, fetcher, options)
```
## 参数
-- `key`: 请求的唯一 key string(或者是 function / array / null) [(高级用法)](/docs/conditional-fetching)
+- `key`: 请求的唯一 key string(或者是 function / array / null) [(详情)](/docs/arguments), [(高级用法)](/docs/conditional-fetching)
- `fetcher`:(_可选_)一个请求数据的 Promise 返回函数 [(详情)](/docs/data-fetching)
- `options`:(_可选_)该 SWR hook 的选项对象
@@ -16,9 +16,12 @@ const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
- `data`: 通过 `fetcher` 用给定的 key 获取的数据(如未完全加载,返回 undefined)
- `error`: `fetcher` 抛出的错误(或者是 undefined)
+- `isLoading`: if there's an ongoing request and no "loaded data". Fallback data and previous data are not considered "loaded data"
- `isValidating`: 是否有请求或重新验证加载
- `mutate(data?, options?)`: 更改缓存数据的函数 [(详情)](/docs/mutation)
+More information can be found [here](/docs/advanced/understanding).
+
## 选项
- `suspense = false`: 启用 React Suspense 模式 [(详情)](/docs/suspense)
@@ -41,10 +44,12 @@ const { data, error, isValidating, mutate } = useSWR(key, fetcher, options)
- `errorRetryCount`: 错误重试的最大次数
- `fallback`: 多个回退数据的 key-value 对象[(示例)](/docs/with-nextjs)
- `fallbackData`: 此 hook 需要返回的初始数据
+- `keepPreviousData = false`: return the previous key's data until the new data has been loaded [(详情)](/docs/advanced/understanding#return-previous-data-for-better-ux)
- `onLoadingSlow(key, config)`: 请求加载时间过长时的回调函数(参考 `loadingTimeout`)
- `onSuccess(data, key, config)`: 请求成功完成时的回调函数
- `onError(err, key, config)`: 请求返回错误时的回调函数
- `onErrorRetry(err, key, config, revalidate, revalidateOps)`: 错误重试的处理函数
+- `onDiscarded(key)`: callback function when a request is ignored due to race conditions
- `compare(a, b)`: 比较函数,用来检测返回的数据何时已更改,以防止伪造的重新渲染。默认情况下使用 [stable-hash](https://github.com/shuding/stable-hash)。
- `isPaused()`: 用于暂停所有数据请求,如果返回值为 `true`,请求的数据和错误都会被忽略。默认返回值为 `false`。
- `use`: 中间件函数数组 [(详情)](/docs/middleware)
diff --git a/pages/docs/arguments.en-US.md b/pages/docs/arguments.en-US.md
index 9350c482..ab30f867 100644
--- a/pages/docs/arguments.en-US.md
+++ b/pages/docs/arguments.en-US.md
@@ -23,10 +23,15 @@ even if `token` changes, SWR will still use the same key and return the wrong da
Instead, you can use an **array** as the `key` parameter, which contains multiple arguments of `fetcher`:
```js
-const { data: user } = useSWR(['/api/user', token], fetchWithToken)
+const { data: user } = useSWR(['/api/user', token], ([url, token]) => fetchWithToken(url, token))
```
-The function `fetchWithToken` still accepts the same 2 arguments, but the cache key will also be associated with `token` now.
+The `fetcher` function accepts the `key` parameter as is, and the cache key will also be associated with the entire `key` argument. In the above example, `url` and `token` are both tight to the cache key.
+
+
+ In the previous versions (< 2.0.0), The `fetcher` function will receive the spreaded arguments from original `key` when the `key` argument is array type. E.g., key `[url, token]` will become 2 arguments `(url, token)` for `fetcher` function.
+
+
## Passing Objects
diff --git a/pages/docs/arguments.es-ES.md b/pages/docs/arguments.es-ES.md
index 9d459727..aca519dc 100644
--- a/pages/docs/arguments.es-ES.md
+++ b/pages/docs/arguments.es-ES.md
@@ -22,10 +22,14 @@ Esto es **incorrecto**. Dado que el identificador (también la key del caché) d
En su lugar, puedes utilizar un **array** como parámetro `key`, que contiene múltiples argumentos de `fetcher`:
```js
-const { data: user } = useSWR(['/api/user', token], fetchWithToken)
+const { data: user } = useSWR(['/api/user', token], ([url, token]) => fetchWithToken(url, token))
```
-La función `fetchWithToken` sigue aceptando los mismo 2 argumentos, pero ahora la key del caché también estará asociada al `token`.
+The `fetcher` function accepts the `key` parameter as is, and the cache key will also be associated with the entire `key` argument. In the above example, `url` and `token` are both tight to the cache key.
+
+
+ In older versions (< 2), The `fetcher` function accepts the `key` parameter as arguments separately
+
## Pasar objetos
diff --git a/pages/docs/arguments.ja.md b/pages/docs/arguments.ja.md
index 6630740e..d69160bf 100644
--- a/pages/docs/arguments.ja.md
+++ b/pages/docs/arguments.ja.md
@@ -23,10 +23,14 @@ useSWR('/api/user', url => fetchWithToken(url, token))
代わりに、`fetcher` の複数の引数を含む**配列**を `key` パラメーターとして使用できます。
```js
-const { data: user } = useSWR(['/api/user', token], fetchWithToken)
+const { data: user } = useSWR(['/api/user', token], ([url, token]) => fetchWithToken(url, token))
```
-この関数 `fetchWithToken` は引き続き同じ二つの引数を受け取りますが、キャッシュキーも `token` と関連付けられます。
+`fetcher` 関数は `key` パラメータをそのまま受け取り、キャッシュキーもまた `key` の引数全てと関連づけられます。上記の例では `url` と `token` の組み合わせがキャッシュキーとなります。
+
+
+ In older versions (< 2), The `fetcher` function accepts the `key` parameter as arguments separately
+
## オブジェクトの受け渡し
diff --git a/pages/docs/arguments.ko.md b/pages/docs/arguments.ko.md
index 1c2a36e1..46af5f17 100644
--- a/pages/docs/arguments.ko.md
+++ b/pages/docs/arguments.ko.md
@@ -23,10 +23,14 @@ useSWR('/api/user', url => fetchWithToken(url, token))
대신에 `fetcher`의 다중 인자를 포함하는 **배열**을 `key` 파라미터로 사용할 수 있습니다.
```js
-const { data: user } = useSWR(['/api/user', token], fetchWithToken)
+const { data: user } = useSWR(['/api/user', token], ([url, token]) => fetchWithToken(url, token))
```
-`fetchWithToken` 함수는 여전히 동일한 두 개의 인자를 받지만, 캐시 키는 이제 `token`과 연결되었습니다.
+The `fetcher` function accepts the `key` parameter as is, and the cache key will also be associated with the entire `key` argument. In the above example, `url` and `token` are both tight to the cache key.
+
+
+ In older versions (< 2), The `fetcher` function accepts the `key` parameter as arguments separately
+
## 객체 전달
diff --git a/pages/docs/arguments.pt-BR.md b/pages/docs/arguments.pt-BR.md
index 02080b66..8f4176eb 100644
--- a/pages/docs/arguments.pt-BR.md
+++ b/pages/docs/arguments.pt-BR.md
@@ -1,56 +1,60 @@
-# Argumentos
-
-Por padrão, `key` será passado para `fetcher` como argumento. Então as 3 expressões a seguir são equivalentes:
-
-```js
-useSWR('/api/user', () => fetcher('/api/user'))
-useSWR('/api/user', url => fetcher(url))
-useSWR('/api/user', fetcher)
-```
-
-## Múltiplos Argumentos
-
-Em alguns cenários, é útil passar vários argumentos (pode ser qualquer valor ou objeto) para a função `fetcher`.
-Por exemplo, uma requisição de fetch autorizada:
-
-```js
-useSWR('/api/user', url => fetchWithToken(url, token))
-```
-
-Isso é **incorreto**. Porque o identificador (também a cache key) do dado é `'/api/user'`,
-mesmo se `token` mudar, SWR ainda usará a mesma chave e retornará o dado errado.
-
-Ao invés disso, você pode usar um array como o parâmetro `key`, que contém vários argumentos para a função `fetcher`.
-
-```js
-const { data: user } = useSWR(['/api/user', token], fetchWithToken)
-```
-
-A função `fetcherWithToken` ainda aceita os mesmos 2 argumentos, mas a cache key será associada com `token` agora.
-
-## Passando Objetos
-
-import Callout from 'nextra-theme-docs/callout'
-
-
- Desde a versão 1.1.0, chaves de objeto serão serializadas por baixo dos panos automaticamente.
-
-
-Digamos que você tenha outra função que busca dados com um escopo de usuário: `fetchWithUser(api, user)`. Você pode fazer o seguinte:
-
-```js
-const { data: user } = useSWR(['/api/user', token], fetchWithToken)
-
-// ...e então passá-lo como argumento para outro hook useSWR
-const { data: orders } = useSWR(user ? ['/api/orders', user] : null, fetchWithUser)
-```
-
-Você pode passar diretamente um objeto como chave, e `fetcher` receberá esse objeto também:
-
-```js
-const { data: orders } = useSWR({ url: '/api/orders', args: user }, fetcher)
-```
-
-
- Em versões antigas (< 1.1.0), SWR compara os argumentos **superficialmente** em cada renderização e aciona a revalidação se algum deles foi alterado.
-
+# Argumentos
+
+Por padrão, `key` será passado para `fetcher` como argumento. Então as 3 expressões a seguir são equivalentes:
+
+```js
+useSWR('/api/user', () => fetcher('/api/user'))
+useSWR('/api/user', url => fetcher(url))
+useSWR('/api/user', fetcher)
+```
+
+## Múltiplos Argumentos
+
+Em alguns cenários, é útil passar vários argumentos (pode ser qualquer valor ou objeto) para a função `fetcher`.
+Por exemplo, uma requisição de fetch autorizada:
+
+```js
+useSWR('/api/user', url => fetchWithToken(url, token))
+```
+
+Isso é **incorreto**. Porque o identificador (também a cache key) do dado é `'/api/user'`,
+mesmo se `token` mudar, SWR ainda usará a mesma chave e retornará o dado errado.
+
+Ao invés disso, você pode usar um array como o parâmetro `key`, que contém vários argumentos para a função `fetcher`.
+
+```js
+const { data: user } = useSWR(['/api/user', token], ([url, token]) => fetchWithToken(url, token))
+```
+
+The `fetcher` function accepts the `key` parameter as is, and the cache key will also be associated with the entire `key` argument. In the above example, `url` and `token` are both tight to the cache key.
+
+
+ In older versions (< 2), The `fetcher` function accepts the `key` parameter as arguments separately
+
+
+## Passando Objetos
+
+import Callout from 'nextra-theme-docs/callout'
+
+
+ Desde a versão 1.1.0, chaves de objeto serão serializadas por baixo dos panos automaticamente.
+
+
+Digamos que você tenha outra função que busca dados com um escopo de usuário: `fetchWithUser(api, user)`. Você pode fazer o seguinte:
+
+```js
+const { data: user } = useSWR(['/api/user', token], fetchWithToken)
+
+// ...e então passá-lo como argumento para outro hook useSWR
+const { data: orders } = useSWR(user ? ['/api/orders', user] : null, fetchWithUser)
+```
+
+Você pode passar diretamente um objeto como chave, e `fetcher` receberá esse objeto também:
+
+```js
+const { data: orders } = useSWR({ url: '/api/orders', args: user }, fetcher)
+```
+
+
+ Em versões antigas (< 1.1.0), SWR compara os argumentos **superficialmente** em cada renderização e aciona a revalidação se algum deles foi alterado.
+
diff --git a/pages/docs/arguments.ru.md b/pages/docs/arguments.ru.md
index 91f0b002..a3f57021 100644
--- a/pages/docs/arguments.ru.md
+++ b/pages/docs/arguments.ru.md
@@ -22,10 +22,14 @@ useSWR('/api/user', url => fetchWithToken(url, token))
Вместо этого вы можете использовать **массив** в качестве параметра `key`, который содержит несколько аргументов `fetcher`:
```js
-const { data: user } = useSWR(['/api/user', token], fetchWithToken)
+const { data: user } = useSWR(['/api/user', token], ([url, token]) => fetchWithToken(url, token))
```
-Функция `fetchWithToken` по-прежнему принимает те же 2 аргумента, но ключ кеша теперь также будет связан с `token`.
+The `fetcher` function accepts the `key` parameter as is, and the cache key will also be associated with the entire `key` argument. In the above example, `url` and `token` are both tight to the cache key.
+
+
+ In older versions (< 2), The `fetcher` function accepts the `key` parameter as arguments separately
+
## Передача объектов
diff --git a/pages/docs/arguments.zh-CN.md b/pages/docs/arguments.zh-CN.md
index 44c518eb..6d158cc9 100644
--- a/pages/docs/arguments.zh-CN.md
+++ b/pages/docs/arguments.zh-CN.md
@@ -21,10 +21,14 @@ useSWR('/api/user', url => fetchWithToken(url, token))
相反,你可以使用一个 **数组** 作为参数 `key`,它包含 `fetcher` 的多个参数:
```js
-const { data: user } = useSWR(['/api/user', token], fetchWithToken)
+const { data: user } = useSWR(['/api/user', token], ([url, token]) => fetchWithToken(url, token))
```
-`fetchWithToken` 函数仍然接受同样的2个参数,但现在缓存 key 也将与 `token` 相关联。
+The `fetcher` function accepts the `key` parameter as is, and the cache key will also be associated with the entire `key` argument. In the above example, `url` and `token` are both tight to the cache key.
+
+
+ In older versions (< 2), The `fetcher` function accepts the `key` parameter as arguments separately
+
## 传入对象
diff --git a/pages/docs/error-handling.en-US.mdx b/pages/docs/error-handling.en-US.mdx
index f689ac4e..143a874f 100644
--- a/pages/docs/error-handling.en-US.mdx
+++ b/pages/docs/error-handling.en-US.mdx
@@ -3,7 +3,7 @@ import Link from 'next/link'
# Error Handling
-If an error is thrown inside [`fetcher`](/docs/data-fetching), it will be returned as `error` by the hook.
+If an error is thrown inside [`fetcher`](/docs/data-fetching), it will be returned as `error` by the hook.
```js
const fetcher = url => fetch(url).then(r => r.json())
@@ -16,10 +16,10 @@ The `error` object will be defined if the fetch promise is rejected.
## Status Code and Error Object
-Sometimes we want an API to return an error object alongside the status code.
+Sometimes we want an API to return an error object alongside the status code.
Both of them are useful for the client.
-We can customize our `fetcher` to return more information. If the status code is not `2xx`,
+We can customize our `fetcher` to return more information. If the status code is not `2xx`,
we consider it an error even if it can be parsed as JSON:
```js
@@ -50,7 +50,7 @@ const { data, error } = useSWR('/api/user', fetcher)
Note that data
and error
can exist at the same time. So the UI can display the existing data,
- while knowing the upcoming request has failed.
+ while knowing the upcoming request has failed.
[Here](/examples/error-handling) we have an example.
@@ -60,7 +60,7 @@ const { data, error } = useSWR('/api/user', fetcher)
SWR uses the [exponential backoff algorithm](https://en.wikipedia.org/wiki/Exponential_backoff) to retry the request on error.
The algorithm allows the app to recover from errors quickly, but not waste resources retrying too often.
-You can also override this behavior via the [onErrorRetry](/docs/options#options) option:
+You can also override this behavior via the [onErrorRetry](/docs/api#options) option:
```js
useSWR('/api/user', fetcher, {
@@ -86,9 +86,9 @@ It's also possible to provide it via the [Global Configuration](/docs/global-con
## Global Error Report
-You can always get the `error` object inside the component reactively.
+You can always get the `error` object inside the component reactively.
But in case you want to handle the error globally, to notify the UI to show a [toast](https://vercel.com/design/toast) or a [snackbar](https://material.io/components/snackbars), or report it somewhere such as [Sentry](https://sentry.io),
-there's an [`onError`](/docs/options#options) event:
+there's an [`onError`](/docs/api#options) event:
```jsx
fetch(url).then(r => res.json())
+const fetcher = url => fetch(url).then(r => res.json())
// ...
const { data, error } = useSWR('/api/user', fetcher)
@@ -16,7 +16,7 @@ El objeto `error` será definido si la promise de fetch es rechazada.
## Código de estado y objeto de error
-A veces queremos que una API devuelva un objeto de error junto con el status code.
+A veces queremos que una API devuelva un objeto de error junto con el status code.
Ambos son útiles para el cliente.
Podemos personanilizar nuestro `fetcher` para que devuelve más información. Si el status code no es `2xx`, lo consideramos
@@ -61,7 +61,7 @@ const { data, error } = useSWR('/api/user', fetcher)
SWR utiliza el [exponential backoff algorithm](https://en.wikipedia.org/wiki/Exponential_backoff) para reintentar la solicitud en el error.
El algoritmo permite que la aplicación se recupere de los errores rápidamente, pero si malgastar recursos reintentando con demasiada frecuencia.
-También podemos anular este comportamiento mediante la opción [onErrorRetry](/docs/options#options):
+También podemos anular este comportamiento mediante la opción [onErrorRetry](/docs/api#options):
```js
useSWR('/api/user', fetcher, {
@@ -87,9 +87,9 @@ También es posible propocionar a través del [Global Configuration](/docs/globa
## Informe global de errores
-Siempre puedes obtener el objeto de `error` dentro del componente de forma reactiva. Pero en caso de que quieras manejar el error de forma global,
-para notificar a la UI que muestre un [toast](https://vercel.com/design/toast) o un [snackbar](https://material.io/components/snackbars), o reportarlo
-en algún lugar como [Sentry](https://sentry.io), hay un evento [`onError`](/docs/options#options):
+Siempre puedes obtener el objeto de `error` dentro del componente de forma reactiva. Pero en caso de que quieras manejar el error de forma global,
+para notificar a la UI que muestre un [toast](https://vercel.com/design/toast) o un [snackbar](https://material.io/components/snackbars), o reportarlo
+en algún lugar como [Sentry](https://sentry.io), hay un evento [`onError`](/docs/api#options):
```jsx
fetch(url).then(r => r.json())
-
-// ...
-const { data, error } = useSWR('/api/user', fetcher)
-```
-
-O objeto `error` será definido se a fetch promise for rejeitada.
-
-## Código de Status e Objeto de Erro
-
-As vezes queremos uma API para retornar um objeto de erro junto ao código de status.
-Ambos são úteis para o cliente.
-
-Nós podemos customizar nossa função `fetcher` para retornar mais informações. Se o código de status não for `2xx`,
-consideramos que é um erro mesmo se ele puder ser lido como JSON:
-
-```js
-const fetcher = async url => {
- const res = await fetch(url)
-
- // Se o código de status não estiver no raio 200-299,
- // nós ainda tentamos ler o JSON e retornar o objeto de erro
- if (!res.ok) {
- const error = new Error('An error occurred while fetching the data.')
- // Adicionar informação extra ao objeto de erro.
- error.info = await res.json()
- error.status = res.status
- throw error
- }
-
- return res.json()
-}
-
-// ...
-const { data, error } = useSWR('/api/user', fetcher)
-// error.info === {
-// message: "You are not authorized to access this resource.",
-// documentation_url: "..."
-// }
-// error.status === 403
-```
-
-
- Note que data
e error
podem existir ao mesmo tempo. Então a UI pode mostrar os dados existentes,
- enquanto sabe que o próximo pedido falhou.
-
-
-[Aqui](/examples/error-handling) nós temos um exemplo.
-
-## Retentativas
-
-SWR usa o [algoritmo de retentativa exponencial (em inglês)](https://en.wikipedia.org/wiki/Exponential_backoff) para tentar novamente a requisição em caso de erro.
-O algoritmo permite a aplicação se recuperar rapidamente de erros, mas não gasta muitos recursos tentando novamente muito frequentemente.
-
-Você também pode sobrescrever este comportamento através da opção [onErrorRetry](/docs/options#options):
-
-```js
-useSWR('/api/user', fetcher, {
- onErrorRetry: (error, key, config, revalidate, { retryCount }) => {
- // Nunca tentar ao 404.
- if (error.status === 404) return
-
- // Nunca tentar para uma chave específica
- if (key === '/api/user') return
-
- // Tentar até 10 vezes.
- if (retryCount >= 10) return
-
- // Tentar novamente depois de 5 segundos
- setTimeout(() => revalidate({ retryCount }), 5000)
- }
-})
-```
-
-Esse callback dá a você a flexibilidade de tentar novamente baseado em várias condições. Você também pode desativá-lo definindo `shouldRetryOnError: false`.
-
-Também é possível provê-lo via contexto de [Configuração Global](/docs/global-configuration).
-
-## Relatório de Erros Globais
-
-Você pode sempre obter o objeto de erro dentro do componente reativamente.
-Mas, no caso de você querer lidar com o erro globalmente, para notificar a UI para mostrar um [toast](https://vercel.com/design/toast) ou [snackbar](https://material.io/components/snackbars), ou reportá-lo em algum lugar como [Sentry](https://sentry.io),
-há um evento chamado [`onError`](/docs/options#options):
-
-```jsx
- {
- if (error.status !== 403 && error.status !== 404) {
- // Nós podemos enviar o erro para Sentry,
- // ou mostrar uma notificação na interface.
- }
- }
-}}>
-
-
-```
+import Callout from 'nextra-theme-docs/callout'
+import Link from 'next/link'
+
+# Manipulação de Erros
+
+Se um erro é lançado dentro de [`fetcher`](/docs/data-fetching), ele será retornado como `error` pelo hook.
+
+```js
+const fetcher = url => fetch(url).then(r => r.json())
+
+// ...
+const { data, error } = useSWR('/api/user', fetcher)
+```
+
+O objeto `error` será definido se a fetch promise for rejeitada.
+
+## Código de Status e Objeto de Erro
+
+As vezes queremos uma API para retornar um objeto de erro junto ao código de status.
+Ambos são úteis para o cliente.
+
+Nós podemos customizar nossa função `fetcher` para retornar mais informações. Se o código de status não for `2xx`,
+consideramos que é um erro mesmo se ele puder ser lido como JSON:
+
+```js
+const fetcher = async url => {
+ const res = await fetch(url)
+
+ // Se o código de status não estiver no raio 200-299,
+ // nós ainda tentamos ler o JSON e retornar o objeto de erro
+ if (!res.ok) {
+ const error = new Error('An error occurred while fetching the data.')
+ // Adicionar informação extra ao objeto de erro.
+ error.info = await res.json()
+ error.status = res.status
+ throw error
+ }
+
+ return res.json()
+}
+
+// ...
+const { data, error } = useSWR('/api/user', fetcher)
+// error.info === {
+// message: "You are not authorized to access this resource.",
+// documentation_url: "..."
+// }
+// error.status === 403
+```
+
+
+ Note que data
e error
podem existir ao mesmo tempo. Então a UI pode mostrar os dados existentes,
+ enquanto sabe que o próximo pedido falhou.
+
+
+[Aqui](/examples/error-handling) nós temos um exemplo.
+
+## Retentativas
+
+SWR usa o [algoritmo de retentativa exponencial (em inglês)](https://en.wikipedia.org/wiki/Exponential_backoff) para tentar novamente a requisição em caso de erro.
+O algoritmo permite a aplicação se recuperar rapidamente de erros, mas não gasta muitos recursos tentando novamente muito frequentemente.
+
+Você também pode sobrescrever este comportamento através da opção [onErrorRetry](/docs/api#options):
+
+```js
+useSWR('/api/user', fetcher, {
+ onErrorRetry: (error, key, config, revalidate, { retryCount }) => {
+ // Nunca tentar ao 404.
+ if (error.status === 404) return
+
+ // Nunca tentar para uma chave específica
+ if (key === '/api/user') return
+
+ // Tentar até 10 vezes.
+ if (retryCount >= 10) return
+
+ // Tentar novamente depois de 5 segundos
+ setTimeout(() => revalidate({ retryCount }), 5000)
+ }
+})
+```
+
+Esse callback dá a você a flexibilidade de tentar novamente baseado em várias condições. Você também pode desativá-lo definindo `shouldRetryOnError: false`.
+
+Também é possível provê-lo via contexto de [Configuração Global](/docs/global-configuration).
+
+## Relatório de Erros Globais
+
+Você pode sempre obter o objeto de erro dentro do componente reativamente.
+Mas, no caso de você querer lidar com o erro globalmente, para notificar a UI para mostrar um [toast](https://vercel.com/design/toast) ou [snackbar](https://material.io/components/snackbars), ou reportá-lo em algum lugar como [Sentry](https://sentry.io),
+há um evento chamado [`onError`](/docs/api#options):
+
+```jsx
+ {
+ if (error.status !== 403 && error.status !== 404) {
+ // Nós podemos enviar o erro para Sentry,
+ // ou mostrar uma notificação na interface.
+ }
+ }
+}}>
+
+
+```
diff --git a/pages/docs/error-handling.ru.mdx b/pages/docs/error-handling.ru.mdx
index 11d2fbd7..3e69b968 100644
--- a/pages/docs/error-handling.ru.mdx
+++ b/pages/docs/error-handling.ru.mdx
@@ -48,8 +48,8 @@ const { data, error } = useSWR('/api/user', fetcher)
```
- Обратите внимание, что data
и error
могут существовать одновременно.
- Таким образом, пользовательский интерфейс может отображать существующие данные, зная,
+ Обратите внимание, что data
и error
могут существовать одновременно.
+ Таким образом, пользовательский интерфейс может отображать существующие данные, зная,
что предстоящий запрос потерпел неудачу.
@@ -60,7 +60,7 @@ const { data, error } = useSWR('/api/user', fetcher)
SWR использует [алгоритм экспоненциальной выдержки](https://ru.wikipedia.org/wiki/Экспоненциальная_выдержка) для повторной попытки запроса в случае ошибки.
Алгоритм позволяет приложению быстро восстанавливаться после ошибок, но не тратить ресурсы на повторные попытки слишком часто.
-Вы также можете изменить это поведение с помощью опции [onErrorRetry](/docs/options#опции):
+Вы также можете изменить это поведение с помощью опции [onErrorRetry](/docs/api#опции):
```js
useSWR('/api/user', fetcher, {
@@ -90,7 +90,7 @@ useSWR('/api/user', fetcher, {
Но в случае, если вы хотите обработать ошибку глобально, чтобы уведомить UI,
чтобы показать [toast](https://vercel.com/design/toast) или [snackbar](https://material.io/components/snackbars),
или сообщить об этом куда-нибудь, например в [Sentry](https://sentry.io),
-есть событие [`onError`](/docs/options#опции):
+есть событие [`onError`](/docs/api#опции):
```jsx
fetch(url).then(r => r.json())
@@ -47,7 +47,7 @@ const { data, error } = useSWR('/api/user', fetcher)
```
- 注意:data
和 error
可以同时存在。所以 UI 可以在知道即将到来的请求失败时显示现有数据。
+ 注意:data
和 error
可以同时存在。所以 UI 可以在知道即将到来的请求失败时显示现有数据。
[这里](/examples/error-handling)有一个示例。
@@ -56,7 +56,7 @@ const { data, error } = useSWR('/api/user', fetcher)
在出现错误时 SWR 使用[指数退避算法](https://en.wikipedia.org/wiki/Exponential_backoff)重发请求。该算法允许应用从错误中快速恢复,而不会浪费资源频繁地重试。
-你还可以通过 [onErrorRetry](/docs/options#options) 选项覆盖该行为:
+你还可以通过 [onErrorRetry](/docs/api#options) 选项覆盖该行为:
```js
useSWR('/api/user', fetcher, {
@@ -82,7 +82,7 @@ useSWR('/api/user', fetcher, {
## 全局错误报告
-你总是可以响应性的在组件内部得到 `error` 对象。但如果你想要全局处理错误,通知 UI 显示一个 [toast](https://vercel.com/design/toast) 或者一个 [snackbar](https://material.io/components/snackbars),或在某处报告它,比如 [Sentry](https://sentry.io),可以用 [`onError`](/docs/options#options) 事件:
+你总是可以响应性的在组件内部得到 `error` 对象。但如果你想要全局处理错误,通知 UI 显示一个 [toast](https://vercel.com/design/toast) 或者一个 [snackbar](https://material.io/components/snackbars),或在某处报告它,比如 [Sentry](https://sentry.io),可以用 [`onError`](/docs/api#options) 事件:
```jsx
fetch(...args).then(res => res.json())
```
- If you want to use GraphQL API or libs like Axios, you can create your own fetcher function.
+ If you want to use GraphQL API or libs like Axios, you can create your own fetcher function.
Check here for more examples.
@@ -36,17 +36,17 @@ Then you can import `useSWR` and start using it inside any function components:
import useSWR from 'swr'
function Profile () {
- const { data, error } = useSWR('/api/user/123', fetcher)
+ const { data, error, isLoading } = useSWR('/api/user/123', fetcher)
if (error) return failed to load
- if (!data) return loading...
+ if (isLoading) return loading...
// render data
return hello {data.name}!
}
```
-Normally, there're 3 possible states of a request: "loading", "ready", or "error". You can use the value of `data` and `error` to
+Normally, there're 3 possible states of a request: "loading", "ready", or "error". You can use the value of `data`, `error` and `isLoading` to
determine the current state of the request, and return the corresponding UI.
## Make It Reusable
@@ -56,11 +56,11 @@ on top of SWR:
```jsx
function useUser (id) {
- const { data, error } = useSWR(`/api/user/${id}`, fetcher)
+ const { data, error, isLoading } = useSWR(`/api/user/${id}`, fetcher)
return {
user: data,
- isLoading: !error && !data,
+ isLoading,
isError: error
}
}
diff --git a/pages/docs/getting-started.es-ES.mdx b/pages/docs/getting-started.es-ES.mdx
index 4cbf60cb..c5cccfab 100644
--- a/pages/docs/getting-started.es-ES.mdx
+++ b/pages/docs/getting-started.es-ES.mdx
@@ -37,10 +37,10 @@ Luego puede importar `useSWR` y empezar a usarlo dentro de cualquier componente
import useSWR from "swr"
function Profile () {
- const { data, error } = useSWR("/api/user/123", fetcher)
+ const { data, error, isLoading } = useSWR("/api/user/123", fetcher)
if (error) return failed to load
- if (!data) return loading...
+ if (isLoading) return loading...
// renderizar datos
return hello {data.name}!
@@ -48,7 +48,7 @@ function Profile () {
```
Normalmente, hay 3 estados posibles de una solicitud: "loading", "ready", o "error". Puedes utilizar el valor
-`data` y `error` para determinar el estado actual de la solicitud, y devolver la UI correspondiente.
+`data`, `error` y `isLoading` para determinar el estado actual de la solicitud, y devolver la UI correspondiente.
## Hágalo reutilizable
@@ -58,11 +58,11 @@ Es increíblemente fácil crear hooks de datos reutilizables sobre SWR:
```jsx
function useUser (id) {
- const { data, error } = useSWR(`/api/user/${id}`, fetcher)
+ const { data, error, isLoading } = useSWR(`/api/user/${id}`, fetcher)
return {
user: data,
- isLoading: !error && !data,
+ isLoading,
isError: error
}
}
diff --git a/pages/docs/getting-started.ja.mdx b/pages/docs/getting-started.ja.mdx
index 3da3a6bd..7b39843c 100644
--- a/pages/docs/getting-started.ja.mdx
+++ b/pages/docs/getting-started.ja.mdx
@@ -27,7 +27,7 @@ const fetcher = (...args) => fetch(...args).then(res => res.json())
もし GraphQL API または Axios のようなライブラリを使いたい場合は、独自のフェッチャー関数を作ることができます。
- その他の例はこちら をご覧ください。
+ その他の例はこちら をご覧ください。
そして、 `useSWR` をインポートして、任意の関数コンポーネント内で使用することができます:
@@ -36,17 +36,17 @@ const fetcher = (...args) => fetch(...args).then(res => res.json())
import useSWR from 'swr'
function Profile () {
- const { data, error } = useSWR('/api/user/123', fetcher)
+ const { data, error, isLoading } = useSWR('/api/user/123', fetcher)
if (error) return failed to load
- if (!data) return loading...
+ if (isLoading) return loading...
// データをレンダリングする
return hello {data.name}!
}
```
-通常、 リクエストには "loading" 、 "ready" 、 "error" の三つの状態があります。 `data` と `error` の値を使ってリクエストの現在の状態を判断し、
+通常、 リクエストには "loading" 、 "ready" 、 "error" の三つの状態があります。 `data` と `error` と `isLoading` の値を使ってリクエストの現在の状態を判断し、
対応する UI を返すことができます。
## 再利用可能にする
@@ -55,11 +55,11 @@ function Profile () {
```jsx
function useUser (id) {
- const { data, error } = useSWR(`/api/user/${id}`, fetcher)
+ const { data, error, isLoading } = useSWR(`/api/user/${id}`, fetcher)
return {
user: data,
- isLoading: !error && !data,
+ isLoading,
isError: error
}
}
diff --git a/pages/docs/getting-started.ko.mdx b/pages/docs/getting-started.ko.mdx
index ec896fb0..90c2b8d4 100644
--- a/pages/docs/getting-started.ko.mdx
+++ b/pages/docs/getting-started.ko.mdx
@@ -26,7 +26,7 @@ const fetcher = (...args) => fetch(...args).then(res => res.json())
```
- GraphQL API 또는 Axios와 같은 라이브러리를 사용하려면 여러분만의 fetcher 함수를 생성하면 됩니다.
+ GraphQL API 또는 Axios와 같은 라이브러리를 사용하려면 여러분만의 fetcher 함수를 생성하면 됩니다.
여기 에서 더 많은 예시를 확인하세요.
@@ -36,18 +36,18 @@ const fetcher = (...args) => fetch(...args).then(res => res.json())
import useSWR from 'swr'
function Profile () {
- const { data, error } = useSWR('/api/user/123', fetcher)
+ const { data, error, isLoading } = useSWR('/api/user/123', fetcher)
if (error) return failed to load
- if (!data) return loading...
+ if (isLoading) return loading...
// 데이터 렌더링
return hello {data.name}!
}
```
-일반적으로, 세 가지 요청 상태가 가능합니다: "loading", "ready", "error". `data`와 `error` 값을 사용해
-현재 요청의 상태를 알아내고, 해당하는 UI를 반환할 수 있습니다.
+Normally, there're 3 possible states of a request: "loading", "ready", or "error". You can use the value of `data`, `error` and `isLoading` to
+determine the current state of the request, and return the corresponding UI.
## 재사용 가능하게 만들기
@@ -56,11 +56,11 @@ function Profile () {
```jsx
function useUser (id) {
- const { data, error } = useSWR(`/api/user/${id}`, fetcher)
+ const { data, error, isLoading } = useSWR(`/api/user/${id}`, fetcher)
return {
user: data,
- isLoading: !error && !data,
+ isLoading,
isError: error
}
}
diff --git a/pages/docs/getting-started.pt-BR.mdx b/pages/docs/getting-started.pt-BR.mdx
index 0a445aa8..5535f502 100644
--- a/pages/docs/getting-started.pt-BR.mdx
+++ b/pages/docs/getting-started.pt-BR.mdx
@@ -1,185 +1,185 @@
-import Callout from 'nextra-theme-docs/callout'
-import Link from 'next/link'
-
-# Comece a Usar
-
-## Instalação
-
-Dentro do diretório do seu projeto React, execute o seguinte:
-
-```plaintext
-yarn add swr
-```
-
-Ou com npm:
-
-```plaintext
-npm install swr
-```
-
-## Início Rápido
-
-Para APIs RESTful normais com dados JSON, primeiro você precisa criar uma função `fetcher`, que é apenas um wrapper do nativo `fetch`:
-
-```jsx
-const fetcher = (...args) => fetch(...args).then(res => res.json())
-```
-
-
- Se você quer usar APIs GraphQL ou outras bibliotecas como Axios, você pode criar sua própria função fetcher.
- Veja aqui para mais exemplos.
-
-
-Então você pode importar `useSWR` e começar a usá-lo em qualquer componente funcional:
-
-```jsx
-import useSWR from 'swr'
-
-function Profile () {
- const { data, error } = useSWR('/api/user/123', fetcher)
-
- if (error) return falhou ao carregar
- if (!data) return carregando...
-
- // rednerizar dados
- return olá {data.name}!
-}
-```
-
-Normalmente, existem 3 possíveis estados de uma requisição: "loading", "ready", ou "error". Você pode usar o valor de `data` e `error`
-para determinar o estado atual da requisição, e retornar a interface correspondente.
-
-## Torne-o Reutilizável
-
-Quando construir uma aplicação web, você pode precisar reutilizar os dados em vários lugares da interface do usuário. É muito fácil criar
-reutilizações de dados em cima do SWR:
-
-```jsx
-function useUser (id) {
- const { data, error } = useSWR(`/api/user/${id}`, fetcher)
-
- return {
- user: data,
- isLoading: !error && !data,
- isError: error
- }
-}
-```
-
-E usá-lo em seus componentes:
-
-```jsx
-function Avatar ({ id }) {
- const { user, isLoading, isError } = useUser(id)
-
- if (isLoading) return
- if (isError) return
- return
-}
-```
-
-Adotando esse padrão, você pode esquecer sobre o **fetching** de dados no modo imperativo: inicie a requisição, atualize o estado de carregamento e retorne o resultado final.
-Ao invés, seu código é mais declarativo: você só precisa especificar o que dados é usado pelo componente.
-
-## Exemplo
-
-Num exemplo real, o nosso site mostra uma barra de navegação e o conteúdo, ambos dependem de `user`:
-
-import { Welcome } from 'components/diagrams/welcome'
-
-
-
-
-
-Tradicionalmente, nós carregamos dados uma vez usando `useEffect` no componente principal, e passamos os dados para os componentes
-filhos via props (note que nós não tratamos o estado de erro por agora).
-
-```jsx {7-11,17,18,27}
-// componente da página
-
-function Page () {
- const [user, setUser] = useState(null)
-
- // fetch data
- useEffect(() => {
- fetch('/api/user')
- .then(res => res.json())
- .then(data => setUser(data))
- }, [])
-
- // global loading state
- if (!user) return
-
- return
-
-
-
-}
-
-// child components
-
-function Navbar ({ user }) {
- return
-}
-
-function Content ({ user }) {
- return Bem vindo de volta, {user.name}
-}
-
-function Avatar ({ user }) {
- return
-}
-```
-
-Normalmente, nós precisamos manter todos os dados de carregamento no componente principal e adicionar props para todos os componentes.
-O código fica mais difícil de manter se adicionarmos mais dependências de dados à página.
-
-Apesar que possamos evitar passar props para componentes filhos, isto é, usar [Context](https://reactjs.org/docs/context.html), ainda há o problema do conteúdo dinâmico:
-componentes dentro da página podem ser dinâmicos, e o componente principal pode não saber quais dados serão necessários por seus componentes filhos.
-
-SWR resolve o problema perfeitamente. Com o hook `useUser`, o código pode ser refatorado para:
-
-```jsx {20,26}
-// page component
-
-function Page () {
- return
-
-
-
-}
-
-// child components
-
-function Navbar () {
- return
-}
-
-function Content () {
- const { user, isLoading } = useUser()
- if (isLoading) return
- return Welcome back, {user.name}
-}
-
-function Avatar () {
- const { user, isLoading } = useUser()
- if (isLoading) return
- return
-}
-```
-
-Os dados agora estão **vinculados** aos componentes que precisam dos dados, e todos os componentes são **independentes** uns dos outros.
-Todos os componentes pai não precisam saber nada sobre os dados ou transmitir dados. Eles apenas renderizam.
-O código é muito mais simples e fácil de manter agora.
-
-O mais bonito é que haverá apenas **1 solicitação** enviada para a API, pois eles usam a mesma chave SWR e
-a solicitação é **duplicada**, **armazenada em cache** e **compartilhada** automaticamente.
-
-Além disso, o aplicativo agora tem a capacidade de buscar novamente os dados ao [focar do usuário ou reconexão de rede](/docs/revalidation)!
-Isso significa que, quando o laptop do usuário sair do modo de suspensão ou alternar entre as guias do navegador, os dados serão atualizados automaticamente.
+import Callout from 'nextra-theme-docs/callout'
+import Link from 'next/link'
+
+# Comece a Usar
+
+## Instalação
+
+Dentro do diretório do seu projeto React, execute o seguinte:
+
+```plaintext
+yarn add swr
+```
+
+Ou com npm:
+
+```plaintext
+npm install swr
+```
+
+## Início Rápido
+
+Para APIs RESTful normais com dados JSON, primeiro você precisa criar uma função `fetcher`, que é apenas um wrapper do nativo `fetch`:
+
+```jsx
+const fetcher = (...args) => fetch(...args).then(res => res.json())
+```
+
+
+ Se você quer usar APIs GraphQL ou outras bibliotecas como Axios, você pode criar sua própria função fetcher.
+ Veja aqui para mais exemplos.
+
+
+Então você pode importar `useSWR` e começar a usá-lo em qualquer componente funcional:
+
+```jsx
+import useSWR from 'swr'
+
+function Profile () {
+ const { data, error, isLoading } = useSWR('/api/user/123', fetcher)
+
+ if (error) return falhou ao carregar
+ if (isLoading) return carregando...
+
+ // rednerizar dados
+ return olá {data.name}!
+}
+```
+
+Normalmente, existem 3 possíveis estados de uma requisição: "loading", "ready", ou "error". Você pode usar o valor de `data`, `error` e `isLoading`
+para determinar o estado atual da requisição, e retornar a interface correspondente.
+
+## Torne-o Reutilizável
+
+Quando construir uma aplicação web, você pode precisar reutilizar os dados em vários lugares da interface do usuário. É muito fácil criar
+reutilizações de dados em cima do SWR:
+
+```jsx
+function useUser (id) {
+ const { data, error, isLoading } = useSWR(`/api/user/${id}`, fetcher)
+
+ return {
+ user: data,
+ isLoading,
+ isError: error
+ }
+}
+```
+
+E usá-lo em seus componentes:
+
+```jsx
+function Avatar ({ id }) {
+ const { user, isLoading, isError } = useUser(id)
+
+ if (isLoading) return
+ if (isError) return
+ return
+}
+```
+
+Adotando esse padrão, você pode esquecer sobre o **fetching** de dados no modo imperativo: inicie a requisição, atualize o estado de carregamento e retorne o resultado final.
+Ao invés, seu código é mais declarativo: você só precisa especificar o que dados é usado pelo componente.
+
+## Exemplo
+
+Num exemplo real, o nosso site mostra uma barra de navegação e o conteúdo, ambos dependem de `user`:
+
+import { Welcome } from 'components/diagrams/welcome'
+
+
+
+
+
+Tradicionalmente, nós carregamos dados uma vez usando `useEffect` no componente principal, e passamos os dados para os componentes
+filhos via props (note que nós não tratamos o estado de erro por agora).
+
+```jsx {7-11,17,18,27}
+// componente da página
+
+function Page () {
+ const [user, setUser] = useState(null)
+
+ // fetch data
+ useEffect(() => {
+ fetch('/api/user')
+ .then(res => res.json())
+ .then(data => setUser(data))
+ }, [])
+
+ // global loading state
+ if (!user) return
+
+ return
+
+
+
+}
+
+// child components
+
+function Navbar ({ user }) {
+ return
+}
+
+function Content ({ user }) {
+ return Bem vindo de volta, {user.name}
+}
+
+function Avatar ({ user }) {
+ return
+}
+```
+
+Normalmente, nós precisamos manter todos os dados de carregamento no componente principal e adicionar props para todos os componentes.
+O código fica mais difícil de manter se adicionarmos mais dependências de dados à página.
+
+Apesar que possamos evitar passar props para componentes filhos, isto é, usar [Context](https://reactjs.org/docs/context.html), ainda há o problema do conteúdo dinâmico:
+componentes dentro da página podem ser dinâmicos, e o componente principal pode não saber quais dados serão necessários por seus componentes filhos.
+
+SWR resolve o problema perfeitamente. Com o hook `useUser`, o código pode ser refatorado para:
+
+```jsx {20,26}
+// page component
+
+function Page () {
+ return
+
+
+
+}
+
+// child components
+
+function Navbar () {
+ return
+}
+
+function Content () {
+ const { user, isLoading } = useUser()
+ if (isLoading) return
+ return Welcome back, {user.name}
+}
+
+function Avatar () {
+ const { user, isLoading } = useUser()
+ if (isLoading) return
+ return
+}
+```
+
+Os dados agora estão **vinculados** aos componentes que precisam dos dados, e todos os componentes são **independentes** uns dos outros.
+Todos os componentes pai não precisam saber nada sobre os dados ou transmitir dados. Eles apenas renderizam.
+O código é muito mais simples e fácil de manter agora.
+
+O mais bonito é que haverá apenas **1 solicitação** enviada para a API, pois eles usam a mesma chave SWR e
+a solicitação é **duplicada**, **armazenada em cache** e **compartilhada** automaticamente.
+
+Além disso, o aplicativo agora tem a capacidade de buscar novamente os dados ao [focar do usuário ou reconexão de rede](/docs/revalidation)!
+Isso significa que, quando o laptop do usuário sair do modo de suspensão ou alternar entre as guias do navegador, os dados serão atualizados automaticamente.
diff --git a/pages/docs/getting-started.ru.mdx b/pages/docs/getting-started.ru.mdx
index 192a9acf..caffc054 100644
--- a/pages/docs/getting-started.ru.mdx
+++ b/pages/docs/getting-started.ru.mdx
@@ -27,7 +27,7 @@ const fetcher = (...args) => fetch(...args).then(res => res.json())
```
- Если вы хотите использовать GraphQL API или библиотеки, такие как Axios, вы можете создать свою
+ Если вы хотите использовать GraphQL API или библиотеки, такие как Axios, вы можете создать свою
собственную fetcher-функцию.
Смотрите примеры здесь .
@@ -38,10 +38,10 @@ const fetcher = (...args) => fetch(...args).then(res => res.json())
import useSWR from 'swr'
function Profile () {
- const { data, error } = useSWR('/api/user/123', fetcher)
+ const { data, error, isLoading } = useSWR('/api/user/123', fetcher)
if (error) return ошибка загрузки
- if (!data) return загрузка...
+ if (isLoading) return загрузка...
// рендер данных
return привет, {data.name}!
@@ -49,7 +49,7 @@ function Profile () {
```
Обычно существует 3 возможных состояния запроса: «загрузка», «готово» или «ошибка». Вы можете использовать
-значение `data` и `error`, чтобы определить текущее состояние запроса и вернуть соответствующий UI.
+значение `data`, `error` и `isLoading`, чтобы определить текущее состояние запроса и вернуть соответствующий UI.
## Делайте многоразовыми
@@ -58,11 +58,11 @@ function Profile () {
```jsx
function useUser (id) {
- const { data, error } = useSWR(`/api/user/${id}`, fetcher)
+ const { data, error, isLoading } = useSWR(`/api/user/${id}`, fetcher)
return {
user: data,
- isLoading: !error && !data,
+ isLoading,
isError: error
}
}
diff --git a/pages/docs/getting-started.zh-CN.mdx b/pages/docs/getting-started.zh-CN.mdx
index 467f7599..10a7bd92 100644
--- a/pages/docs/getting-started.zh-CN.mdx
+++ b/pages/docs/getting-started.zh-CN.mdx
@@ -35,17 +35,17 @@ const fetcher = (...args) => fetch(...args).then((res) => res.json())
import useSWR from "swr";
function Profile() {
- const { data, error } = useSWR("/api/user/123", fetcher)
+ const { data, error, isLoading } = useSWR("/api/user/123", fetcher)
if (error) return failed to load
- if (!data) return loading...
+ if (isLoading) return loading...
// 渲染数据
return hello {data.name}!
}
```
-通常,一个请求有 3 种可能的状态:“loading”、“ready”或“error”。你可以使用 `data` 和 `error` 的值来确定当前的请求状态,并返回相应的 UI。
+通常,一个请求有 3 种可能的状态:“loading”、“ready”或“error”。你可以使用 `data`、`error` 和 `isLoading` 的值来确定当前的请求状态,并返回相应的 UI。
## 可复用组件
@@ -53,11 +53,11 @@ function Profile() {
```jsx
function useUser(id) {
- const { data, error } = useSWR(`/api/user/${id}`, fetcher)
+ const { data, error, isLoading } = useSWR(`/api/user/${id}`, fetcher)
return {
user: data,
- isLoading: !error && !data,
+ isLoading,
isError: error,
}
}
diff --git a/pages/docs/global-configuration.en-US.md b/pages/docs/global-configuration.en-US.md
index ccaf0414..8f4cb82b 100644
--- a/pages/docs/global-configuration.en-US.md
+++ b/pages/docs/global-configuration.en-US.md
@@ -1,6 +1,6 @@
# Global Configuration
-The context `SWRConfig` can provide global configurations ([options](/docs/options)) for all SWR hooks.
+The context `SWRConfig` can provide global configurations ([options](/docs/api)) for all SWR hooks.
```jsx
@@ -35,11 +35,86 @@ function App () {
}
```
+## Nesting Configurations
+
+`SWRConfig` merges the configuration from the parent context. It can receive either an object or a functional configuration. The functional one receives the parent configuration as argument and returns a new configuration that you can customize it yourself.
+
+### Object Configuration Example
+
+```jsx
+import { SWRConfig, useSWRConfig } from 'swr'
+
+function App() {
+ return (
+
+
+
+
+
+ )
+}
+
+function Page() {
+ const config = useSWRConfig()
+ // {
+ // dedupingInterval: 200,
+ // refreshInterval: 100,
+ // fallback: { a: 2, b: 1, c: 2 },
+ // }
+}
+```
+
+### Functional Configuration Example
+
+```jsx
+import { SWRConfig, useSWRConfig } from 'swr'
+
+function App() {
+ return (
+
+ ({
+ dedupingInterval: parent.dedupingInterval * 5,
+ fallback: { a: 2, c: 2 },
+ })}
+ >
+
+
+
+ )
+}
+
+function Page() {
+ const config = useSWRConfig()
+ // {
+ // dedupingInterval: 500,
+ // fallback: { a: 2, c: 2 },
+ // }
+}
+```
+
## Extra APIs
### Cache Provider
-Besides all the [options](/docs/options) listed, `SWRConfig` also accepts an optional `provider` function. Please refer to the [Cache](/docs/advanced/cache) section for more details.
+Besides all the [options](/docs/api) listed, `SWRConfig` also accepts an optional `provider` function. Please refer to the [Cache](/docs/advanced/cache) section for more details.
```jsx
new Map() }}>
diff --git a/pages/docs/global-configuration.es-ES.md b/pages/docs/global-configuration.es-ES.md
index 367a74a6..d64473be 100644
--- a/pages/docs/global-configuration.es-ES.md
+++ b/pages/docs/global-configuration.es-ES.md
@@ -1,6 +1,6 @@
# Configuración Global
-El contexto `SWRConfig` puede proporcionar configuraciones globales ([opciones](/docs/options)) para todos los hooks de SWR.
+El contexto `SWRConfig` puede proporcionar configuraciones globales ([opciones](/docs/api)) para todos los hooks de SWR.
```jsx
@@ -36,11 +36,86 @@ function App () {
}
```
+## Nesting Configurations
+
+`SWRConfig` merges the configuration from the parent context. It can receive either an object or a functional configuration. The functional one receives the parent configuration as argument and returns a new configuration that you can customize it yourself.
+
+### Object Configuration Example
+
+```jsx
+import { SWRConfig, useSWRConfig } from 'swr'
+
+function App() {
+ return (
+
+
+
+
+
+ )
+}
+
+function Page() {
+ const config = useSWRConfig()
+ // {
+ // dedupingInterval: 200,
+ // refreshInterval: 100,
+ // fallback: { a: 2, b: 1, c: 2 },
+ // }
+}
+```
+
+### Functional Configuration Example
+
+```jsx
+import { SWRConfig, useSWRConfig } from 'swr'
+
+function App() {
+ return (
+
+ ({
+ dedupingInterval: parent.dedupingInterval * 5,
+ fallback: { a: 2, c: 2 },
+ })}
+ >
+
+
+
+ )
+}
+
+function Page() {
+ const config = useSWRConfig()
+ // {
+ // dedupingInterval: 500,
+ // fallback: { a: 2, c: 2 },
+ // }
+}
+```
+
## Extra APIs
### Cache Provider
-Besides all the [options](/docs/options) listed, `SWRConfig` also accepts an optional `provider` function. Please refer to the [Cache](/docs/cache) section for more details.
+Besides all the [options](/docs/api) listed, `SWRConfig` also accepts an optional `provider` function. Please refer to the [Cache](/docs/cache) section for more details.
```jsx
new Map() }}>
diff --git a/pages/docs/global-configuration.ja.md b/pages/docs/global-configuration.ja.md
index f3f01739..c3e98894 100644
--- a/pages/docs/global-configuration.ja.md
+++ b/pages/docs/global-configuration.ja.md
@@ -1,6 +1,6 @@
# グローバルな設定
-`SWRConfig` コンテキストによって、すべての SWR フックに対するグローバルな設定( [オプション](/docs/options) )を提供できます。
+`SWRConfig` コンテキストによって、すべての SWR フックに対するグローバルな設定( [オプション](/docs/api) )を提供できます。
```jsx
@@ -35,6 +35,78 @@ function App () {
}
```
+## ネストした設定
+
+`SWRConfig` は親で指定された設定をマージします。設定はオブジェクトまたは関数として受け取ることができます。関数の場合、親の設定を引数として受け取り新しくカスタマイズした設定を返します。
+
+```jsx
+import { SWRConfig, useSWRConfig } from 'swr'
+
+function App() {
+ return (
+
+
+
+
+
+ )
+}
+
+function Page() {
+ const config = useSWRConfig()
+ // {
+ // dedupingInterval: 200,
+ // refreshInterval: 100,
+ // fallback: { a: 2, b: 1, c: 2 },
+ // }
+}
+```
+
+
+```jsx
+import { SWRConfig, useSWRConfig } from 'swr'
+
+function App() {
+ return (
+
+ ({
+ dedupingInterval: parent.dedupingInterval * 5,
+ fallback: { a: 2, c: 2 },
+ })}
+ >
+
+
+
+ )
+}
+
+function Page() {
+ const config = useSWRConfig()
+ // {
+ // dedupingInterval: 500,
+ // fallback: { a: 2, c: 2 },
+ // }
+}
+```
+
## Extra APIs
### キャッシュプロバイダー
diff --git a/pages/docs/global-configuration.ko.md b/pages/docs/global-configuration.ko.md
index 419d09b1..35a78b04 100644
--- a/pages/docs/global-configuration.ko.md
+++ b/pages/docs/global-configuration.ko.md
@@ -1,6 +1,6 @@
# 전역 설정
-`SWRconfig` 컨텍스트는 모든 SWR hook에 대한 전역 설정([options](/docs/options))을 제공합니다.
+`SWRconfig` 컨텍스트는 모든 SWR hook에 대한 전역 설정([options](/docs/api))을 제공합니다.
```jsx
@@ -35,11 +35,86 @@ function App () {
}
```
+## Nesting Configurations
+
+`SWRConfig` merges the configuration from the parent context. It can receive either an object or a functional configuration. The functional one receives the parent configuration as argument and returns a new configuration that you can customize it yourself.
+
+### Object Configuration Example
+
+```jsx
+import { SWRConfig, useSWRConfig } from 'swr'
+
+function App() {
+ return (
+
+
+
+
+
+ )
+}
+
+function Page() {
+ const config = useSWRConfig()
+ // {
+ // dedupingInterval: 200,
+ // refreshInterval: 100,
+ // fallback: { a: 2, b: 1, c: 2 },
+ // }
+}
+```
+
+### Functional Configuration Example
+
+```jsx
+import { SWRConfig, useSWRConfig } from 'swr'
+
+function App() {
+ return (
+
+ ({
+ dedupingInterval: parent.dedupingInterval * 5,
+ fallback: { a: 2, c: 2 },
+ })}
+ >
+
+
+
+ )
+}
+
+function Page() {
+ const config = useSWRConfig()
+ // {
+ // dedupingInterval: 500,
+ // fallback: { a: 2, c: 2 },
+ // }
+}
+```
+
## 부가적인 APIs
### 캐시 공급자
-나열된 모든 [옵션](/docs/options) 외에도, `SWRConfig`는 선택적으로 `provider` 함수도 받습니다. 더 자세한 내용은 [캐시](/docs/advanced/cache) 섹션을 참고해 주세요.
+나열된 모든 [옵션](/docs/api) 외에도, `SWRConfig`는 선택적으로 `provider` 함수도 받습니다. 더 자세한 내용은 [캐시](/docs/advanced/cache) 섹션을 참고해 주세요.
```jsx
new Map() }}>
diff --git a/pages/docs/global-configuration.pt-BR.md b/pages/docs/global-configuration.pt-BR.md
index 902fd710..3cc924d0 100644
--- a/pages/docs/global-configuration.pt-BR.md
+++ b/pages/docs/global-configuration.pt-BR.md
@@ -1,64 +1,139 @@
-# Configuração Global
-
-O contexto `SWRConfig` pode fornecer configurações globais ([opções](/docs/options)) para todos os hooks SWR.
-
-```jsx
-
-
-
-```
-
-Neste exemplo, todos os hooks SWR usarão o mesmo fetcher fornecido para carregar dados JSON, e atualizar a cada 3 segundos por padrão:
-
-```jsx
-import useSWR, { SWRConfig } from 'swr'
-
-function Dashboard () {
- const { data: events } = useSWR('/api/events')
- const { data: projects } = useSWR('/api/projects')
- const { data: user } = useSWR('/api/user', { refreshInterval: 0 }) // override
-
- // ...
-}
-
-function App () {
- return (
- fetch(resource, init).then(res => res.json())
- }}
- >
-
-
- )
-}
-```
-
-## APIs Extras
-
-### Cache Provider
-
-Além de todas as [opções](/docs/options) listadas, `SWRConfig` também aceita uma função opcional `provider`. Consulte a seção [Cache](/docs/advanced/cache) para obter mais detalhes.
-
-```jsx
- new Map() }}>
-
-
-```
-
-### Acesso às Configurações Globais
-
-Você pode usar o hook `useSWRConfig` para obter as configurações globais, assim como [`mutate`](/docs/mutation) e [`cache`](/docs/advanced/cache):
-
-```jsx
-import { useSWRConfig } from 'swr'
-
-function Component () {
- const { refreshInterval, mutate, cache, ...restConfig } = useSWRConfig()
-
- // ...
-}
-```
-
-Configurações aninhadas serão extendidas. Se não houver nenhum `` usado, será retornado as configurações padrão.
+# Configuração Global
+
+O contexto `SWRConfig` pode fornecer configurações globais ([opções](/docs/api)) para todos os hooks SWR.
+
+```jsx
+
+
+
+```
+
+Neste exemplo, todos os hooks SWR usarão o mesmo fetcher fornecido para carregar dados JSON, e atualizar a cada 3 segundos por padrão:
+
+```jsx
+import useSWR, { SWRConfig } from 'swr'
+
+function Dashboard () {
+ const { data: events } = useSWR('/api/events')
+ const { data: projects } = useSWR('/api/projects')
+ const { data: user } = useSWR('/api/user', { refreshInterval: 0 }) // override
+
+ // ...
+}
+
+function App () {
+ return (
+ fetch(resource, init).then(res => res.json())
+ }}
+ >
+
+
+ )
+}
+```
+
+## Nesting Configurations
+
+`SWRConfig` merges the configuration from the parent context. It can receive either an object or a functional configuration. The functional one receives the parent configuration as argument and returns a new configuration that you can customize it yourself.
+
+### Object Configuration Example
+
+```jsx
+import { SWRConfig, useSWRConfig } from 'swr'
+
+function App() {
+ return (
+
+
+
+
+
+ )
+}
+
+function Page() {
+ const config = useSWRConfig()
+ // {
+ // dedupingInterval: 200,
+ // refreshInterval: 100,
+ // fallback: { a: 2, b: 1, c: 2 },
+ // }
+}
+```
+
+### Functional Configuration Example
+
+```jsx
+import { SWRConfig, useSWRConfig } from 'swr'
+
+function App() {
+ return (
+
+ ({
+ dedupingInterval: parent.dedupingInterval * 5,
+ fallback: { a: 2, c: 2 },
+ })}
+ >
+
+
+
+ )
+}
+
+function Page() {
+ const config = useSWRConfig()
+ // {
+ // dedupingInterval: 500,
+ // fallback: { a: 2, c: 2 },
+ // }
+}
+```
+
+## APIs Extras
+
+### Cache Provider
+
+Além de todas as [opções](/docs/api) listadas, `SWRConfig` também aceita uma função opcional `provider`. Consulte a seção [Cache](/docs/advanced/cache) para obter mais detalhes.
+
+```jsx
+ new Map() }}>
+
+
+```
+
+### Acesso às Configurações Globais
+
+Você pode usar o hook `useSWRConfig` para obter as configurações globais, assim como [`mutate`](/docs/mutation) e [`cache`](/docs/advanced/cache):
+
+```jsx
+import { useSWRConfig } from 'swr'
+
+function Component () {
+ const { refreshInterval, mutate, cache, ...restConfig } = useSWRConfig()
+
+ // ...
+}
+```
+
+Configurações aninhadas serão extendidas. Se não houver nenhum `` usado, será retornado as configurações padrão.
diff --git a/pages/docs/global-configuration.ru.md b/pages/docs/global-configuration.ru.md
index 2a56bb6d..faa42b81 100644
--- a/pages/docs/global-configuration.ru.md
+++ b/pages/docs/global-configuration.ru.md
@@ -1,6 +1,6 @@
# Глобальная конфигурация
-Контекст `SWRConfig` может предоставить глобальные конфигурации ([опции](/docs/options)) для всех SWR хуков.
+Контекст `SWRConfig` может предоставить глобальные конфигурации ([опции](/docs/api)) для всех SWR хуков.
```jsx
@@ -35,11 +35,86 @@ function App () {
}
```
+## Nesting Configurations
+
+`SWRConfig` merges the configuration from the parent context. It can receive either an object or a functional configuration. The functional one receives the parent configuration as argument and returns a new configuration that you can customize it yourself.
+
+### Object Configuration Example
+
+```jsx
+import { SWRConfig, useSWRConfig } from 'swr'
+
+function App() {
+ return (
+
+
+
+
+
+ )
+}
+
+function Page() {
+ const config = useSWRConfig()
+ // {
+ // dedupingInterval: 200,
+ // refreshInterval: 100,
+ // fallback: { a: 2, b: 1, c: 2 },
+ // }
+}
+```
+
+### Functional Configuration Example
+
+```jsx
+import { SWRConfig, useSWRConfig } from 'swr'
+
+function App() {
+ return (
+
+ ({
+ dedupingInterval: parent.dedupingInterval * 5,
+ fallback: { a: 2, c: 2 },
+ })}
+ >
+
+
+
+ )
+}
+
+function Page() {
+ const config = useSWRConfig()
+ // {
+ // dedupingInterval: 500,
+ // fallback: { a: 2, c: 2 },
+ // }
+}
+```
+
## Дополнительные API
### Провайдер кеша
-Помимо всех перечисленных [опций](/docs/options), `SWRConfig` также принимает опциональную функцию `provider`.
+Помимо всех перечисленных [опций](/docs/api), `SWRConfig` также принимает опциональную функцию `provider`.
Пожалуйста, обратитесь к разделу [Кэш](/docs/advanced/cache) для более подробной информации.
```jsx
diff --git a/pages/docs/global-configuration.zh-CN.md b/pages/docs/global-configuration.zh-CN.md
index 32f636c6..ca43f726 100644
--- a/pages/docs/global-configuration.zh-CN.md
+++ b/pages/docs/global-configuration.zh-CN.md
@@ -1,6 +1,6 @@
# 全局配置
-`SWRConfig` 可以为所有的 SWR hook 提供全局配置 ([选项](/docs/options))。
+`SWRConfig` 可以为所有的 SWR hook 提供全局配置 ([选项](/docs/api))。
```jsx
@@ -35,11 +35,86 @@ function App () {
}
```
+## Nesting Configurations
+
+`SWRConfig` merges the configuration from the parent context. It can receive either an object or a functional configuration. The functional one receives the parent configuration as argument and returns a new configuration that you can customize it yourself.
+
+### Object Configuration Example
+
+```jsx
+import { SWRConfig, useSWRConfig } from 'swr'
+
+function App() {
+ return (
+
+
+
+
+
+ )
+}
+
+function Page() {
+ const config = useSWRConfig()
+ // {
+ // dedupingInterval: 200,
+ // refreshInterval: 100,
+ // fallback: { a: 2, b: 1, c: 2 },
+ // }
+}
+```
+
+### Functional Configuration Example
+
+```jsx
+import { SWRConfig, useSWRConfig } from 'swr'
+
+function App() {
+ return (
+
+ ({
+ dedupingInterval: parent.dedupingInterval * 5,
+ fallback: { a: 2, c: 2 },
+ })}
+ >
+
+
+
+ )
+}
+
+function Page() {
+ const config = useSWRConfig()
+ // {
+ // dedupingInterval: 500,
+ // fallback: { a: 2, c: 2 },
+ // }
+}
+```
+
## 额外的 API
### 缓存 Provider
-除去以上所列的 [选项](/docs/options),`SWRConfig` 还接受一个可选的 `provider` 函数。详细信息请参考 [缓存](/docs/advanced/cache) 这一节。
+除去以上所列的 [选项](/docs/api),`SWRConfig` 还接受一个可选的 `provider` 函数。详细信息请参考 [缓存](/docs/advanced/cache) 这一节。
```jsx
new Map() }}>
diff --git a/pages/docs/meta.en-US.json b/pages/docs/meta.en-US.json
index f8e7cb34..54d8f278 100644
--- a/pages/docs/meta.en-US.json
+++ b/pages/docs/meta.en-US.json
@@ -1,13 +1,13 @@
{
"getting-started": "Getting Started",
- "options": "Options",
+ "api": "API",
"global-configuration": "Global Configuration",
"data-fetching": "Data Fetching",
- "error-handling": "Error Handling",
"revalidation": "Auto Revalidation",
- "conditional-fetching": "Conditional Data Fetching",
"arguments": "Arguments",
- "mutation": "Mutation",
+ "mutation": "Mutation & Revalidation",
+ "error-handling": "Error Handling",
+ "conditional-fetching": "Conditional Data Fetching",
"pagination": "Pagination",
"prefetching": "Prefetching",
"with-nextjs": "Next.js SSG and SSR",
diff --git a/pages/docs/meta.es-ES.json b/pages/docs/meta.es-ES.json
index 89bd42b3..05a01ece 100644
--- a/pages/docs/meta.es-ES.json
+++ b/pages/docs/meta.es-ES.json
@@ -1,6 +1,6 @@
{
"getting-started": "Comienza",
- "options": "Opciones",
+ "api": "API",
"global-configuration": "Configuración Global",
"data-fetching": "Obtención De Datos",
"error-handling": "Gestión De Errores",
diff --git a/pages/docs/meta.ja.json b/pages/docs/meta.ja.json
index dc330822..4899177c 100644
--- a/pages/docs/meta.ja.json
+++ b/pages/docs/meta.ja.json
@@ -1,13 +1,13 @@
{
"getting-started": "はじめに",
- "options": "オプション",
+ "api": "API",
"global-configuration": "グローバルな設定",
"data-fetching": "データの取得",
"error-handling": "エラーハンドリング",
"revalidation": "自動再検証",
"conditional-fetching": "条件付きフェッチ",
"arguments": "引数",
- "mutation": "ミューテーション",
+ "mutation": "ミューテーションと再検証",
"pagination": "ページネーション",
"prefetching": "プリフェッチ",
"with-nextjs": "Next.js の SSG と SSR",
diff --git a/pages/docs/meta.ko.json b/pages/docs/meta.ko.json
index 2ff6ecac..6101ded6 100644
--- a/pages/docs/meta.ko.json
+++ b/pages/docs/meta.ko.json
@@ -1,6 +1,6 @@
{
"getting-started": "시작하기",
- "options": "옵션",
+ "api": "API",
"global-configuration": "전역 설정",
"data-fetching": "데이터 가져오기",
"error-handling": "에러 처리",
diff --git a/pages/docs/meta.pt-BR.json b/pages/docs/meta.pt-BR.json
index b88dceb8..aca9c9f1 100644
--- a/pages/docs/meta.pt-BR.json
+++ b/pages/docs/meta.pt-BR.json
@@ -1,19 +1,19 @@
-{
- "getting-started": "Comece a Usar",
- "options": "Opções",
- "global-configuration": "Configuração Global",
- "data-fetching": "Data Fetching",
- "error-handling": "Manipulação de Erros",
- "revalidation": "Revalidação",
- "conditional-fetching": "Data Fetching Condicional",
- "arguments": "Argumentos",
- "mutation": "Mutação",
- "pagination": "Paginação",
- "prefetching": "Prefetching",
- "with-nextjs": "SSG e SSR com Next.js",
- "typescript": "TypeScript",
- "suspense": "Suspense",
- "middleware": "Middleware",
- "advanced": "Avançado",
- "change-log": "Change Log"
-}
+{
+ "getting-started": "Comece a Usar",
+ "api": "API",
+ "global-configuration": "Configuração Global",
+ "data-fetching": "Data Fetching",
+ "error-handling": "Manipulação de Erros",
+ "revalidation": "Revalidação",
+ "conditional-fetching": "Data Fetching Condicional",
+ "arguments": "Argumentos",
+ "mutation": "Mutação",
+ "pagination": "Paginação",
+ "prefetching": "Prefetching",
+ "with-nextjs": "SSG e SSR com Next.js",
+ "typescript": "TypeScript",
+ "suspense": "Suspense",
+ "middleware": "Middleware",
+ "advanced": "Avançado",
+ "change-log": "Change Log"
+}
diff --git a/pages/docs/meta.ru.json b/pages/docs/meta.ru.json
index d947fc5c..06795af2 100644
--- a/pages/docs/meta.ru.json
+++ b/pages/docs/meta.ru.json
@@ -1,6 +1,6 @@
{
"getting-started": "Начало работы",
- "options": "Опции",
+ "api": "API",
"global-configuration": "Глобальная конфигурация",
"data-fetching": "Выборка данных",
"error-handling": "Обработка ошибок",
diff --git a/pages/docs/meta.zh-CN.json b/pages/docs/meta.zh-CN.json
index 52eab9ec..7b774e96 100644
--- a/pages/docs/meta.zh-CN.json
+++ b/pages/docs/meta.zh-CN.json
@@ -1,6 +1,6 @@
{
"getting-started": "入门",
- "options": "选项",
+ "api": "API",
"global-configuration": "全局配置",
"data-fetching": "数据请求",
"error-handling": "错误处理",
diff --git a/pages/docs/mutation.en-US.md b/pages/docs/mutation.en-US.md
index a46135a9..678cb505 100644
--- a/pages/docs/mutation.en-US.md
+++ b/pages/docs/mutation.en-US.md
@@ -1,25 +1,69 @@
-# Mutation
+# Mutation & Revalidation
+
+SWR provides the [`mutate`](/docs/mutation#mutate) and [`useSWRMutation`](/docs/mutation#useswrmutation) APIs for mutating remote data and related cache.
+
+## `mutate`
+
+There're 2 ways to use the `mutate` API to mutate the data, the global mutate API which can mutate any key and the bound mutate API which only can mutate the data of corresponding SWR hook.
+
+#### Global Mutate
+
+The recommended way to get the global mutator is to use the [`useSWRConfig`](/docs/global-configuration#access-to-global-configurations) hook:
```js
-mutate(key, data, options)
+import { useSWRConfig } from "swr"
+
+function App() {
+ const { mutate } = useSWRConfig()
+ mutate(key, data, options)
+}
```
-## Options
+You can also import it globally:
-- `optimisticData`: data to immediately update the client cache, usually used in optimistic UI.
-- `revalidate`: should the cache revalidate once the asynchronous update resolves.
-- `populateCache`: should the result of the remote mutation be written to the cache, or a function that receives new result and current result as arguments and returns the mutation result.
-- `rollbackOnError`: should the cache rollback if the remote mutation errors.
+```js
+import { mutate } from "swr"
-## Revalidate
+function App() {
+ mutate(key, data, options)
+}
+```
-You can get the `mutate` function from the `useSWRConfig()` hook, and broadcast a revalidation message
-globally to other SWR hooks\* using the same key by calling `mutate(key)`.
+#### Bound Mutate
-This example shows how to automatically refetch the login info (e.g. inside ` `)
-when the user clicks the “Logout” button.
+Bound mutate is the short path to mutate the current key with data. Which `key` is bounded to the `key` passing to `useSWR`, and receive the `data` as the first argument.
+
+It is functionally equivalent to the global `mutate` function in the previous section but does not require the `key` parameter:
```jsx
+import useSWR from 'swr'
+
+function Profile () {
+ const { data, mutate } = useSWR('/api/user', fetcher)
+
+ return (
+
+
My name is {data.name}.
+ {
+ const newName = data.name.toUpperCase()
+ // send a request to the API to update the data
+ await requestUpdateUsername(newName)
+ // update the local data immediately and revalidate (refetch)
+ // NOTE: key is not required when using useSWR's mutate as it's pre-bound
+ mutate({ ...data, name: newName })
+ }}>Uppercase my name!
+
+ )
+}
+```
+
+#### Revalidation
+
+When you call `mutate(key)` (or just `mutate()` with the bound mutate API) without any data, it will trigger a revalidation (mark the data as expired and trigger a refetch)
+for the resource. This example shows how to automatically refetch the login info (e.g. inside ` `)
+when the user clicks the “Logout” button:
+
+```jsx {14}
import useSWR, { useSWRConfig } from 'swr'
function App () {
@@ -42,15 +86,180 @@ function App () {
}
```
-\*: _It broadcasts to SWR hooks under the same [cache provider](/docs/advanced/cache) scope. If no cache provider exists, it will broadcast to all SWR hooks._
+import Callout from 'nextra-theme-docs/callout'
+
+
+It broadcasts to SWR hooks under the same [cache provider](/docs/advanced/cache) scope. If no cache provider exists, it will broadcast to all SWR hooks.
+
+
+
+### API
+
+#### Parameters
+
+- `key`: same as `useSWR`'s `key`, but a function behaves as [a filter function](/docs/mutation#mutate-multiple-items)
+- `data`: data to update the client cache, or an async function for the remote mutation
+- `options`: accepts the following options
+ - `optimisticData`: data to immediately update the client cache, or a function that receives current data and returns the new client cache data, usually used in optimistic UI.
+ - `revalidate = true`: should the cache revalidate once the asynchronous update resolves.
+ - `populateCache = true`: should the result of the remote mutation be written to the cache, or a function that receives new result and current result as arguments and returns the mutation result.
+ - `rollbackOnError = true`: should the cache rollback if the remote mutation errors, or a function that receives the error thrown from fetcher as arguments and returns a boolean whether should rollback or not.
+ - `throwOnError = true`: should the mutate call throw the error when fails.
+
+#### Return Values
+
+`mutate` returns the results the `data` parameter has been resolved. The function passed to `mutate` will return an updated data which is used to update the corresponding cache value. If there is an error thrown while executing the function, the error will be thrown so it can be handled appropriately.
+
+```jsx
+try {
+ const user = await mutate('/api/user', updateUser(newUser))
+} catch (error) {
+ // Handle an error while updating the user here
+}
+```
+
+## `useSWRMutation`
+
+SWR also provides `useSWRMutation` as a hook for remote mutations. The remote mutations are only triggered manually, instead of automatically like `useSWR`.
+
+Also, this hook doesn’t share states with other `useSWRMutation` hooks.
+
+```jsx
+import useSWRMutation from 'swr/mutation'
+
+// Fetcher implementation.
+// The extra argument will be passed via the `arg` property of the 2nd parameter.
+// In the example below, `arg` will be `'my_token'`
+async function updateUser(url, { arg }) {
+ await fetch(url, {
+ method: 'POST',
+ headers: {
+ Authorization: `Bearer ${arg}`
+ }
+ })
+}
+
+function Profile() {
+ // A useSWR + mutate like API, but it will not start the request automatically.
+ const { trigger } = useSWRMutation('/api/user', updateUser, options?)
+
+ return {
+ // Trigger `updateUser` with a specific argument.
+ trigger('my_token')
+ }}>Update User
+}
+```
+
+### API
+
+#### Parameters
+
+- `key`: same as [`mutate`](/docs/mutation#mutate)'s `key`
+- `fetcher(key, { arg })`: an async function for remote mutation
+- `options`: an optional object with the following properties:
+ - `optimisticData`: same as `mutate`'s `optimisticData`
+ - `revalidate = true`: same as `mutate`'s `revalidate`
+ - `populateCache = false`: same as `mutate`'s `populateCache`, but the default is `false`
+ - `rollbackOnError = true`: same as `mutate`'s `rollbackOnError`
+ - `throwOnError = true`: same as `mutate`'s `throwOnError`
+ - `onSuccess(data, key, config)`: callback function when a remote mutation has been finished successfully
+ - `onError(err, key, config)`: callback function when a remote mutation has returned an error
+
+#### Return Values
+
+- `data`: data for the given key returned from `fetcher`
+- `error`: error thrown by `fetcher` (or undefined)
+- `trigger(arg, options)`: a function to trigger a remote mutation
+- `reset`: a function to reset the state (`data`, `error`, `isMutating`)
+- `isMutating`: if there's an ongoing remote mutation
+
+### Basic Usage
+
+```jsx
+import useSWRMutation from 'swr/mutation'
+
+async function sendRequest(url, { arg }) {
+ return fetch(url, {
+ method: 'POST',
+ body: JSON.stringify(arg)
+ })
+}
+
+function App() {
+ const { trigger, isMutating } = useSWRMutation('/api/user', sendRequest, /* options */)
+
+ return (
+ {
+ try {
+ const result = await trigger({ username: 'johndoe' }, /* options */)
+ } catch (e) {
+ // error handling
+ }
+ }}
+ >
+ Create User
+
+ )
+}
+```
+
+If you want to use the mutation results in rendering, you can get them from the return values of `useSWRMutation`.
+
+```jsx
+const { trigger, data, error } = useSWRMutation('/api/user', sendRequest)
+```
+
+`useSWRMutation` shares a cache store with `useSWR`, so it can detect and avoid race conditions between `useSWR`. It also supports `mutate`'s functionalities like optimistic updates and rollback on errors. You can pass these options `useSWRMutation` and its `trigger` function.
+
+```jsx
+const { trigger } = useSWRMutation('/api/user', updateUser, {
+ optimisticData: current => ({ ...current, name: newName })
+})
+
+// or
+
+trigger(newName, {
+ optimisticData: current => ({ ...current, name: newName })
+})
+```
+
+### Defer loading data until needed
+
+You can also use `useSWRMutation` for loading data. `useSWRMutation` never start requesting until `trigger` is called, so you can defer loading data when you actually need it.
+
+```jsx
+import { useState } from 'react'
+import useSWRMutation from 'swr/mutation'
+
+const fetcher = url => fetch(url).then(res => res.json())
+
+const Page = () => {
+ const [show, setShow] = useState(false)
+ // data is undefined until trigger is called
+ const { data: user, trigger } = useSWRMutation('/api/user', fetcher);
+
+ return (
+
+
{
+ trigger();
+ setShow(true);
+ }}>Show User
+ {show && user ?
{usre.name}
: null}
+
+ );
+}
+```
## Optimistic Updates
In many cases, applying local mutations to data is a good way to make changes
feel faster — no need to wait for the remote source of data.
-With `mutate`, you can update your local data programmatically, while
-revalidating and finally replace it with the latest data.
+With the `optimisticData` option, you can update your local data manually, while
+waiting for the remote mutation to finish. Composing `rollbackOnError` you can also
+control when to rollback the data.
```jsx
import useSWR, { useSWRConfig } from 'swr'
@@ -65,7 +274,13 @@ function Profile () {
{
const newName = data.name.toUpperCase()
const user = { ...data, name: newName }
- const options = { optimisticData: user, rollbackOnError: true }
+ const options = {
+ optimisticData: user,
+ rollbackOnError(error) {
+ // If it's timeout abort error, don't rollback
+ return error.name !== 'AbortError'
+ },
+ }
// updates the local data immediately
// send a request to update the data
@@ -79,30 +294,65 @@ function Profile () {
> The **`updateFn`** should be a promise or asynchronous function to handle the remote mutation, it should return updated data.
-## Mutate Based on Current Data
+You can also pass a function to `optimisticData` to make it depending on the current data:
-Sometimes, you want to update a part of your data based on the current data.
+```jsx
+import useSWR, { useSWRConfig } from 'swr'
-With `mutate`, you can pass an async function which will receive the current cached value, if any, and returns an updated document.
+function Profile () {
+ const { mutate } = useSWRConfig()
+ const { data } = useSWR('/api/user', fetcher)
+
+ return (
+
+
My name is {data.name}.
+ {
+ const newName = data.name.toUpperCase()
+ mutate('/api/user', updateUserName(newName), {
+ optimisticData: user => ({ ...user, name: newName }),
+ rollbackOnError: true
+ });
+ }}>Uppercase my name!
+
+ )
+}
+```
+
+You can also create the same thing with `useSWRMutation` and `trigger`:
```jsx
-mutate('/api/todos', async todos => {
- // let's update the todo with ID `1` to be completed,
- // this API returns the updated data
- const updatedTodo = await fetch('/api/todos/1', {
- method: 'PATCH',
- body: JSON.stringify({ completed: true })
- })
+import useSWRMutation from 'swr/mutation'
- // filter the list, and return it with the updated item
- const filteredTodos = todos.filter(todo => todo.id !== '1')
- return [...filteredTodos, updatedTodo]
-// Since the API already gives us the updated information,
-// we don't need to revalidate here.
-}, { revalidate: false })
+function Profile () {
+ const { trigger } = useSWRMutation('/api/user', updateUserName)
+
+ return (
+
+
My name is {data.name}.
+ {
+ const newName = data.name.toUpperCase()
+
+ trigger(newName, {
+ optimisticData: user => ({ ...user, name: newName }),
+ rollbackOnError: true
+ })
+ }}>Uppercase my name!
+
+ )
+}
```
-You can also use the `populateCache` option.
+## Rollback on Errors
+
+When you have `optimisticData` set, it’s possible that the optimistic data gets
+displayed to the user, but the remote mutation fails. In this case, you can leverage
+`rollbackOnError` to revert the local cache to the previous state, to make sure
+the user is seeing the correct data.
+
+## Update Cache After Mutation
+
+Sometimes, the remote mutation request directly returns the updated data, so there is no need to do an extra fetch to load it.
+You can enable the `populateCache` option to update the cache for `useSWR` with the response of the mutation:
```jsx
const updateTodo = () => fetch('/api/todos/1', {
@@ -122,44 +372,120 @@ mutate('/api/todos', updateTodo, {
})
```
-## Returned Data from Mutate
+Or with the `useSWRMutation` hook:
-Most probably, you need some data to update the cache. The data is resolved or returned from the promise or async function you passed to `mutate`.
+```jsx
+useSWRMutation('/api/todos', updateTodo, {
+ populateCache: (updatedTodo, todos) => {
+ // filter the list, and return it with the updated item
+ const filteredTodos = todos.filter(todo => todo.id !== '1')
+ return [...filteredTodos, updatedTodo]
+ },
+ // Since the API already gives us the updated information,
+ // we don't need to revalidate here.
+ revalidate: false
+})
+```
-The function passed to `mutate` will return an updated document which is used to update the corresponding cache value. If there is an error thrown while executing the function, the error will be thrown so it can be handled appropriately.
+When combined with `optimisticData` and `rollbackOnError`, you’ll get a perfect optimistic UI experience.
-```jsx
-try {
- const user = await mutate('/api/user', updateUser(newUser))
-} catch (error) {
- // Handle an error while updating the user here
+## Avoid Race Conditions
+
+Both `mutate` and `useSWRMutation` can avoid race conditions between `useSWR`. For example,
+
+```tsx
+function Profile() {
+ const { data } = useSWR('/api/user', getUser, { revalidateInterval: 3000 })
+ const { trigger } = useSWRMutation('/api/user', updateUser)
+
+ return <>
+ {data ? data.username : null}
+ trigger()}>Update User
+ >
}
```
-## Bound Mutate
+The normal `useSWR` hook might refresh its data any time due to focus, polling, or other conditions. This way the displayed username
+can be as fresh as possible. However, since we have a mutation there that can happen at the nearly same time of a refetch of `useSWR`, there
+could be a race condition that `getUser` request starts earlier, but takes longer than `updateUser`.
+
+Luckily, `useSWRMutation` handles this for you automatically. After the mutation, it will tell `useSWR` to ditch the ongoing request and revalidate,
+so the stale data will never be displayed.
-The SWR object returned by `useSWR` also contains a `mutate()` function that is pre-bound to the SWR's key.
+## Mutate Based on Current Data
+
+Sometimes, you want to update a part of your data based on the current data.
-It is functionally equivalent to the global `mutate` function but does not require the `key` parameter.
+With `mutate`, you can pass an async function which will receive the current cached value, if any, and returns an updated document.
```jsx
-import useSWR from 'swr'
+mutate('/api/todos', async todos => {
+ // let's update the todo with ID `1` to be completed,
+ // this API returns the updated data
+ const updatedTodo = await fetch('/api/todos/1', {
+ method: 'PATCH',
+ body: JSON.stringify({ completed: true })
+ })
-function Profile () {
- const { data, mutate } = useSWR('/api/user', fetcher)
+ // filter the list, and return it with the updated item
+ const filteredTodos = todos.filter(todo => todo.id !== '1')
+ return [...filteredTodos, updatedTodo]
+// Since the API already gives us the updated information,
+// we don't need to revalidate here.
+}, { revalidate: false })
+```
- return (
-
-
My name is {data.name}.
- {
- const newName = data.name.toUpperCase()
- // send a request to the API to update the data
- await requestUpdateUsername(newName)
- // update the local data immediately and revalidate (refetch)
- // NOTE: key is not required when using useSWR's mutate as it's pre-bound
- mutate({ ...data, name: newName })
- }}>Uppercase my name!
-
- )
-}
+## Mutate Multiple Items
+
+The global `mutate` API accepts a filter function, which accepts `key` as the argument and returns which keys to revalidate. The filter function is applied to all the existing cache keys:
+
+```jsx
+import { mutate } from 'swr'
+// Or from the hook if you customized the cache provider:
+// { mutate } = useSWRConfig()
+
+mutate(
+ key => typeof key === 'string' && key.startsWith('/api/item?id='),
+ undefined,
+ { revalidate: true }
+)
+```
+
+This also works with any key type like an array. The mutation matches all keys, of which the first element is `'item'`.
+
+```jsx
+useSWR(['item', 123], ...)
+useSWR(['item', 124], ...)
+useSWR(['item', 125], ...)
+
+mutate(
+ key => Array.isArray(key) && key[0] === 'item',
+ undefined,
+ { revalidate: false }
+)
+```
+
+The filter function is applied to all existing cache keys, so you should not assume the shape of keys when using multiple shapes of keys.
+
+```jsx
+// ✅ matching array key
+mutate((key) => key[0].startsWith('/api'), data)
+// ✅ matching string key
+mutate((key) => typeof key === 'string' && key.startsWith('/api'), data)
+
+// ❌ ERROR: mutate uncertain keys (array or string)
+mutate((key: any) => /\/api/.test(key.toString()))
+```
+
+You can use the filter function to clear all cache data, which is useful when logging out:
+
+```js
+const clearCache = () => mutate(
+ () => true,
+ undefined,
+ { revalidate: false }
+)
+
+// ...clear cache on logout
+clearCache()
```
diff --git a/pages/docs/mutation.es-ES.md b/pages/docs/mutation.es-ES.md
index 38579e98..678cb505 100644
--- a/pages/docs/mutation.es-ES.md
+++ b/pages/docs/mutation.es-ES.md
@@ -1,25 +1,69 @@
-# Mutación
+# Mutation & Revalidation
+
+SWR provides the [`mutate`](/docs/mutation#mutate) and [`useSWRMutation`](/docs/mutation#useswrmutation) APIs for mutating remote data and related cache.
+
+## `mutate`
+
+There're 2 ways to use the `mutate` API to mutate the data, the global mutate API which can mutate any key and the bound mutate API which only can mutate the data of corresponding SWR hook.
+
+#### Global Mutate
+
+The recommended way to get the global mutator is to use the [`useSWRConfig`](/docs/global-configuration#access-to-global-configurations) hook:
```js
-mutate(key, data, options)
+import { useSWRConfig } from "swr"
+
+function App() {
+ const { mutate } = useSWRConfig()
+ mutate(key, data, options)
+}
```
-## Options
+You can also import it globally:
+
+```js
+import { mutate } from "swr"
-- `optimisticData`: data to immediately update the client cache, usually used in optimistic UI.
-- `revalidate`: should the cache revalidate once the asynchronous update resolves.
-- `populateCache`: should the result of the remote mutation be written to the cache, or a function that receives new result and current result as arguments and returns the mutation result.
-- `rollbackOnError`: should the cache rollback if the remote mutation errors.
+function App() {
+ mutate(key, data, options)
+}
+```
-## Revalidar
+#### Bound Mutate
-You can get the `mutate` function from the `useSWRConfig()` hook, and broadcast a revalidation message
-globally to other SWR hooks\* using the same key by calling `mutate(key)`.
+Bound mutate is the short path to mutate the current key with data. Which `key` is bounded to the `key` passing to `useSWR`, and receive the `data` as the first argument.
-Este ejemplo muestra cómo recuperar automáticamente la información de login (por ejemplo, dentro de ` `)
-cuando el usuario hace clic en el botón "Logout".
+It is functionally equivalent to the global `mutate` function in the previous section but does not require the `key` parameter:
```jsx
+import useSWR from 'swr'
+
+function Profile () {
+ const { data, mutate } = useSWR('/api/user', fetcher)
+
+ return (
+
+
My name is {data.name}.
+ {
+ const newName = data.name.toUpperCase()
+ // send a request to the API to update the data
+ await requestUpdateUsername(newName)
+ // update the local data immediately and revalidate (refetch)
+ // NOTE: key is not required when using useSWR's mutate as it's pre-bound
+ mutate({ ...data, name: newName })
+ }}>Uppercase my name!
+
+ )
+}
+```
+
+#### Revalidation
+
+When you call `mutate(key)` (or just `mutate()` with the bound mutate API) without any data, it will trigger a revalidation (mark the data as expired and trigger a refetch)
+for the resource. This example shows how to automatically refetch the login info (e.g. inside ` `)
+when the user clicks the “Logout” button:
+
+```jsx {14}
import useSWR, { useSWRConfig } from 'swr'
function App () {
@@ -29,10 +73,10 @@ function App () {
{
- // establecer la cookie como caduca
+ // set the cookie as expired
document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'
- // indicar a todos SWRs con esta key que se revaliden
+ // tell all SWRs with this key to revalidate
mutate('/api/user')
}}>
Logout
@@ -42,13 +86,180 @@ function App () {
}
```
+import Callout from 'nextra-theme-docs/callout'
+
+
+It broadcasts to SWR hooks under the same [cache provider](/docs/advanced/cache) scope. If no cache provider exists, it will broadcast to all SWR hooks.
+
+
+
+### API
+
+#### Parameters
+
+- `key`: same as `useSWR`'s `key`, but a function behaves as [a filter function](/docs/mutation#mutate-multiple-items)
+- `data`: data to update the client cache, or an async function for the remote mutation
+- `options`: accepts the following options
+ - `optimisticData`: data to immediately update the client cache, or a function that receives current data and returns the new client cache data, usually used in optimistic UI.
+ - `revalidate = true`: should the cache revalidate once the asynchronous update resolves.
+ - `populateCache = true`: should the result of the remote mutation be written to the cache, or a function that receives new result and current result as arguments and returns the mutation result.
+ - `rollbackOnError = true`: should the cache rollback if the remote mutation errors, or a function that receives the error thrown from fetcher as arguments and returns a boolean whether should rollback or not.
+ - `throwOnError = true`: should the mutate call throw the error when fails.
+
+#### Return Values
+
+`mutate` returns the results the `data` parameter has been resolved. The function passed to `mutate` will return an updated data which is used to update the corresponding cache value. If there is an error thrown while executing the function, the error will be thrown so it can be handled appropriately.
+
+```jsx
+try {
+ const user = await mutate('/api/user', updateUser(newUser))
+} catch (error) {
+ // Handle an error while updating the user here
+}
+```
+
+## `useSWRMutation`
+
+SWR also provides `useSWRMutation` as a hook for remote mutations. The remote mutations are only triggered manually, instead of automatically like `useSWR`.
+
+Also, this hook doesn’t share states with other `useSWRMutation` hooks.
+
+```jsx
+import useSWRMutation from 'swr/mutation'
+
+// Fetcher implementation.
+// The extra argument will be passed via the `arg` property of the 2nd parameter.
+// In the example below, `arg` will be `'my_token'`
+async function updateUser(url, { arg }) {
+ await fetch(url, {
+ method: 'POST',
+ headers: {
+ Authorization: `Bearer ${arg}`
+ }
+ })
+}
+
+function Profile() {
+ // A useSWR + mutate like API, but it will not start the request automatically.
+ const { trigger } = useSWRMutation('/api/user', updateUser, options?)
+
+ return {
+ // Trigger `updateUser` with a specific argument.
+ trigger('my_token')
+ }}>Update User
+}
+```
+
+### API
+
+#### Parameters
+
+- `key`: same as [`mutate`](/docs/mutation#mutate)'s `key`
+- `fetcher(key, { arg })`: an async function for remote mutation
+- `options`: an optional object with the following properties:
+ - `optimisticData`: same as `mutate`'s `optimisticData`
+ - `revalidate = true`: same as `mutate`'s `revalidate`
+ - `populateCache = false`: same as `mutate`'s `populateCache`, but the default is `false`
+ - `rollbackOnError = true`: same as `mutate`'s `rollbackOnError`
+ - `throwOnError = true`: same as `mutate`'s `throwOnError`
+ - `onSuccess(data, key, config)`: callback function when a remote mutation has been finished successfully
+ - `onError(err, key, config)`: callback function when a remote mutation has returned an error
+
+#### Return Values
+
+- `data`: data for the given key returned from `fetcher`
+- `error`: error thrown by `fetcher` (or undefined)
+- `trigger(arg, options)`: a function to trigger a remote mutation
+- `reset`: a function to reset the state (`data`, `error`, `isMutating`)
+- `isMutating`: if there's an ongoing remote mutation
+
+### Basic Usage
+
+```jsx
+import useSWRMutation from 'swr/mutation'
+
+async function sendRequest(url, { arg }) {
+ return fetch(url, {
+ method: 'POST',
+ body: JSON.stringify(arg)
+ })
+}
+
+function App() {
+ const { trigger, isMutating } = useSWRMutation('/api/user', sendRequest, /* options */)
+
+ return (
+ {
+ try {
+ const result = await trigger({ username: 'johndoe' }, /* options */)
+ } catch (e) {
+ // error handling
+ }
+ }}
+ >
+ Create User
+
+ )
+}
+```
+
+If you want to use the mutation results in rendering, you can get them from the return values of `useSWRMutation`.
+
+```jsx
+const { trigger, data, error } = useSWRMutation('/api/user', sendRequest)
+```
+
+`useSWRMutation` shares a cache store with `useSWR`, so it can detect and avoid race conditions between `useSWR`. It also supports `mutate`'s functionalities like optimistic updates and rollback on errors. You can pass these options `useSWRMutation` and its `trigger` function.
+
+```jsx
+const { trigger } = useSWRMutation('/api/user', updateUser, {
+ optimisticData: current => ({ ...current, name: newName })
+})
+
+// or
+
+trigger(newName, {
+ optimisticData: current => ({ ...current, name: newName })
+})
+```
+
+### Defer loading data until needed
+
+You can also use `useSWRMutation` for loading data. `useSWRMutation` never start requesting until `trigger` is called, so you can defer loading data when you actually need it.
+
+```jsx
+import { useState } from 'react'
+import useSWRMutation from 'swr/mutation'
+
+const fetcher = url => fetch(url).then(res => res.json())
+
+const Page = () => {
+ const [show, setShow] = useState(false)
+ // data is undefined until trigger is called
+ const { data: user, trigger } = useSWRMutation('/api/user', fetcher);
+
+ return (
+
+
{
+ trigger();
+ setShow(true);
+ }}>Show User
+ {show && user ?
{usre.name}
: null}
+
+ );
+}
+```
+
## Optimistic Updates
In many cases, applying local mutations to data is a good way to make changes
feel faster — no need to wait for the remote source of data.
-With `mutate`, you can update your local data programmatically, while
-revalidating and finally replace it with the latest data.
+With the `optimisticData` option, you can update your local data manually, while
+waiting for the remote mutation to finish. Composing `rollbackOnError` you can also
+control when to rollback the data.
```jsx
import useSWR, { useSWRConfig } from 'swr'
@@ -63,7 +274,13 @@ function Profile () {
{
const newName = data.name.toUpperCase()
const user = { ...data, name: newName }
- const options = { optimisticData: user, rollbackOnError: true }
+ const options = {
+ optimisticData: user,
+ rollbackOnError(error) {
+ // If it's timeout abort error, don't rollback
+ return error.name !== 'AbortError'
+ },
+ }
// updates the local data immediately
// send a request to update the data
@@ -77,30 +294,65 @@ function Profile () {
> The **`updateFn`** should be a promise or asynchronous function to handle the remote mutation, it should return updated data.
-## Mutar basándose en los datos actuales
+You can also pass a function to `optimisticData` to make it depending on the current data:
-A veces, se desea actualizar una parte de los datos en función de los datos actuales.
+```jsx
+import useSWR, { useSWRConfig } from 'swr'
-Con `mutate`, puedes pasar una función asíncrona que recibirá el valor actual de la caché, si lo hay, y devolverá un updated document.
+function Profile () {
+ const { mutate } = useSWRConfig()
+ const { data } = useSWR('/api/user', fetcher)
+
+ return (
+
+
My name is {data.name}.
+ {
+ const newName = data.name.toUpperCase()
+ mutate('/api/user', updateUserName(newName), {
+ optimisticData: user => ({ ...user, name: newName }),
+ rollbackOnError: true
+ });
+ }}>Uppercase my name!
+
+ )
+}
+```
+
+You can also create the same thing with `useSWRMutation` and `trigger`:
```jsx
-mutate('/api/todos', async todos => {
- // actualicemos la tarea con ID `1` para que se complete,
- // esta API devuelve los datos acutualizado
- const updatedTodo = await fetch('/api/todos/1', {
- method: 'PATCH',
- body: JSON.stringify({ completed: true })
- })
+import useSWRMutation from 'swr/mutation'
- // filtrar la lista y devolverla con el item actualizado
- const filteredTodos = todos.filter(todo => todo.id !== '1')
- return [...filteredTodos, updatedTodo]
-// Since the API already gives us the updated information,
-// we don't need to revalidate here.
-}, { revalidate: false })
+function Profile () {
+ const { trigger } = useSWRMutation('/api/user', updateUserName)
+
+ return (
+
+
My name is {data.name}.
+ {
+ const newName = data.name.toUpperCase()
+
+ trigger(newName, {
+ optimisticData: user => ({ ...user, name: newName }),
+ rollbackOnError: true
+ })
+ }}>Uppercase my name!
+
+ )
+}
```
-You can also use the `populateCache` option.
+## Rollback on Errors
+
+When you have `optimisticData` set, it’s possible that the optimistic data gets
+displayed to the user, but the remote mutation fails. In this case, you can leverage
+`rollbackOnError` to revert the local cache to the previous state, to make sure
+the user is seeing the correct data.
+
+## Update Cache After Mutation
+
+Sometimes, the remote mutation request directly returns the updated data, so there is no need to do an extra fetch to load it.
+You can enable the `populateCache` option to update the cache for `useSWR` with the response of the mutation:
```jsx
const updateTodo = () => fetch('/api/todos/1', {
@@ -120,45 +372,120 @@ mutate('/api/todos', updateTodo, {
})
```
-## Datos devueltos por Mutate
+Or with the `useSWRMutation` hook:
-Lo más probable es que necesites algunos datos para actualizar la caché. Los datos se resuelven o se devuelven
-desde la promise o función asíncrona que pasaste para `mutate`.
+```jsx
+useSWRMutation('/api/todos', updateTodo, {
+ populateCache: (updatedTodo, todos) => {
+ // filter the list, and return it with the updated item
+ const filteredTodos = todos.filter(todo => todo.id !== '1')
+ return [...filteredTodos, updatedTodo]
+ },
+ // Since the API already gives us the updated information,
+ // we don't need to revalidate here.
+ revalidate: false
+})
+```
-La función devolverá update document para que `mutate` actualice el valor de la caché correspondiente. Podría arrojar un error de alguna manera, cada vez que se llame.
+When combined with `optimisticData` and `rollbackOnError`, you’ll get a perfect optimistic UI experience.
-```jsx
-try {
- const user = await mutate('/api/user', updateUser(newUser))
-} catch (error) {
- // Manejar un error al actualizar el user aquí
+## Avoid Race Conditions
+
+Both `mutate` and `useSWRMutation` can avoid race conditions between `useSWR`. For example,
+
+```tsx
+function Profile() {
+ const { data } = useSWR('/api/user', getUser, { revalidateInterval: 3000 })
+ const { trigger } = useSWRMutation('/api/user', updateUser)
+
+ return <>
+ {data ? data.username : null}
+ trigger()}>Update User
+ >
}
```
-## Bound Mutate
+The normal `useSWR` hook might refresh its data any time due to focus, polling, or other conditions. This way the displayed username
+can be as fresh as possible. However, since we have a mutation there that can happen at the nearly same time of a refetch of `useSWR`, there
+could be a race condition that `getUser` request starts earlier, but takes longer than `updateUser`.
-El objeto SWR devuelto por `useSWR` también contiene una función `mutate()` que está atada a la key del SWR.
+Luckily, `useSWRMutation` handles this for you automatically. After the mutation, it will tell `useSWR` to ditch the ongoing request and revalidate,
+so the stale data will never be displayed.
-Es funcionalmente equivalente a la función global `mutate` pero no requiere el parámetro `key`.
+## Mutate Based on Current Data
+
+Sometimes, you want to update a part of your data based on the current data.
+
+With `mutate`, you can pass an async function which will receive the current cached value, if any, and returns an updated document.
```jsx
-import useSWR from 'swr'
+mutate('/api/todos', async todos => {
+ // let's update the todo with ID `1` to be completed,
+ // this API returns the updated data
+ const updatedTodo = await fetch('/api/todos/1', {
+ method: 'PATCH',
+ body: JSON.stringify({ completed: true })
+ })
-function Profile () {
- const { data, mutate } = useSWR('/api/user', fetcher)
+ // filter the list, and return it with the updated item
+ const filteredTodos = todos.filter(todo => todo.id !== '1')
+ return [...filteredTodos, updatedTodo]
+// Since the API already gives us the updated information,
+// we don't need to revalidate here.
+}, { revalidate: false })
+```
- return (
-
-
My name is {data.name}.
- {
- const newName = data.name.toUpperCase()
- // enviar una solicitud a la API para actualizar los datos
- await requestUpdateUsername(newName)
- // actualizar los datos locales inmediatamente y revalidar (refetch)
- // NOTA: la key no es necesaria cuando se utiliza el mutate de useSWR, es pre-bound
- mutate({ ...data, name: newName })
- }}>Uppercase my name!
-
- )
-}
+## Mutate Multiple Items
+
+The global `mutate` API accepts a filter function, which accepts `key` as the argument and returns which keys to revalidate. The filter function is applied to all the existing cache keys:
+
+```jsx
+import { mutate } from 'swr'
+// Or from the hook if you customized the cache provider:
+// { mutate } = useSWRConfig()
+
+mutate(
+ key => typeof key === 'string' && key.startsWith('/api/item?id='),
+ undefined,
+ { revalidate: true }
+)
+```
+
+This also works with any key type like an array. The mutation matches all keys, of which the first element is `'item'`.
+
+```jsx
+useSWR(['item', 123], ...)
+useSWR(['item', 124], ...)
+useSWR(['item', 125], ...)
+
+mutate(
+ key => Array.isArray(key) && key[0] === 'item',
+ undefined,
+ { revalidate: false }
+)
+```
+
+The filter function is applied to all existing cache keys, so you should not assume the shape of keys when using multiple shapes of keys.
+
+```jsx
+// ✅ matching array key
+mutate((key) => key[0].startsWith('/api'), data)
+// ✅ matching string key
+mutate((key) => typeof key === 'string' && key.startsWith('/api'), data)
+
+// ❌ ERROR: mutate uncertain keys (array or string)
+mutate((key: any) => /\/api/.test(key.toString()))
+```
+
+You can use the filter function to clear all cache data, which is useful when logging out:
+
+```js
+const clearCache = () => mutate(
+ () => true,
+ undefined,
+ { revalidate: false }
+)
+
+// ...clear cache on logout
+clearCache()
```
diff --git a/pages/docs/mutation.ja.md b/pages/docs/mutation.ja.md
index 40f330bb..302c4fb7 100644
--- a/pages/docs/mutation.ja.md
+++ b/pages/docs/mutation.ja.md
@@ -1,25 +1,69 @@
-# ミューテーション
+# ミューテーションと再検証
+
+SWR はリモートデータとキャッシュデータのミューテーションのために、[`mutate`](/docs/mutation#mutate) と [`useSWRMutation`](/docs/mutation#useswrmutation) の API を提供しています。
+
+## `mutate`
+
+`mutate` API を使いデータをミューテートするには 2 つの方法があります。どんなキーに対してもミューテートできるグローバルのミューテート API と対応する SWR フックのデータのみミューテートできるバウンドミューテート API です。
+
+#### グローバルミューテート
+
+グローバルなミューテートを取得するオススメの方法は [`useSWRConfig`](/docs/global-configuration#access-to-global-configurations) フックを使うことです。
```js
-mutate(key, data, options)
+import { useSWRConfig } from "swr"
+
+function App() {
+ const { mutate } = useSWRConfig()
+ mutate(key, data, options)
+}
```
-## オプション
+またはグローバルにインポートすることもできます。
+
+```js
+import { mutate } from "swr"
-- `optimisticData`: クライアントキャッシュを直ちに更新するデータで、通常は 楽観的な UI で使用されます。
-- `revalidate`: 非同期更新が解決した後、キャッシュを再検証するかどうか。
-- `populateCache`: リモートミューテーションの結果をキャッシュに書き込むかどうか。関数も受け取ります。関数はミューテーションの結果と現在の値を引数として受けとり、ミューテーションの結果を返します。
-- `rollbackOnError`: リモートミューテーションでエラーが発生した場合、キャッシュをロールバックするかどうか。
+function App() {
+ mutate(key, data, options)
+}
+```
-## 再検証
+#### バウンドミューテート
-`useSWRConfig()` フックから `mutate` 関数を取得し、`mutate(key)` を呼び出すことで、
-再検証メッセージを他の SWR フック\* に渡すことができます。
+バウンドミューテートは現在のキーのデータをミューテートする最も簡単な方法です。`useSWR` に渡された `key` に対して対応付けられ、`data` を第一引数として受け取ります。
-次の例では、ユーザーが "Logout" ボタンをクリックしたときに、ログイン情報
-( たとえば ` `の中身 )を自動的に取得する方法を示します。
+機能自体は前のセクションで紹介したグローバルな `mutate` と同じですが、`key` を引数として指定する必要がありません。
```jsx
+import useSWR from 'swr'
+
+function Profile () {
+ const { data, mutate } = useSWR('/api/user', fetcher)
+
+ return (
+
+
My name is {data.name}.
+ {
+ const newName = data.name.toUpperCase()
+ // データを更新するために API にリクエストを送信します
+ await requestUpdateUsername(newName)
+ // ローカルのデータを即座に更新して再検証(再フェッチ)します
+ // 注意: useSWR の mutate は key が対応付けられているため、key の指定は必要ありません
+ mutate({ ...data, name: newName })
+ }}>Uppercase my name!
+
+ )
+}
+```
+
+#### 再検証
+
+`mutate(key)` (または単にバウンドミューテートの `mutate()`) をデータの指定なしに呼んだ場合、そのリソースに対して再検証を発行 (データを期限切れとしてマークして再フェッチを発行する) します。
+この例は、ユーザーが "Logout" ボタンをクリックした場合に、
+ログイン情報 (例: ` ` の中身) をどのように自動的に再フェッチするかを示しています。
+
+```jsx {14}
import useSWR, { useSWRConfig } from 'swr'
function App () {
@@ -32,7 +76,7 @@ function App () {
// クッキーを期限切れとして設定します
document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'
- // このキーを使用してすべての SWR に再検証するように指示します
+ // この key を使っている全ての SWR フックに再検証を伝えます
mutate('/api/user')
}}>
Logout
@@ -42,15 +86,180 @@ function App () {
}
```
-\*: 同じ [キャッシュプロバイダー](advanced/cache) のスコープ下では、 SWR フックに渡されます。もしキャッシュプロバイダーが無ければ、すべての SWR フックに渡されます。
+import Callout from 'nextra-theme-docs/callout'
+
+
+これは同じスコープの [キャッシュプロバイダ](/docs/advanced/cache) 内の SWR フックに対して再検証の指示を一斉送信します。もしキャッシュプロバイダが存在しない場合、全ての SWR フックに対して一斉送信されます。
+
+
+
+### API
+
+#### パラメータ
+
+- `key`: `useSWR` の `key` と同じです。しかしながら、関数は [フィルタ関数](/docs/mutation#mutate-multiple-items) として振る舞います
+- `data`: クライアントキャッシュを更新するためのデータ、またはリモートミューテーションのための非同期関数
+- `options`: 下記のオプションを受け取ります
+ - `optimisticData`: クライアントキャッシュを即座に更新するためのデータ、または現在のデータを受け取り新しいクライアントキャッシュデータを返す関数。楽観的 UI のために使われます
+ - `revalidate = true`: 非同期の更新処理を完了した後にキャッシュの再検証を行うかどうか
+ - `populateCache = true`: リモートミューテーションの結果をキャッシュに書き込むかどうか、またはリモートミューテーションの結果と現在のデータを引数として受け取り、ミューテーションの結果を返す関数
+ - `rollbackOnError = true`: リモートミューテーションがエラーだった場合にキャッシュをロールバックするかどうか、または発生したエラーを引数として受け取りロールバックするかどうかの真偽値を返す関数
+ - `throwOnError = true`: ミューテートの呼び出しが失敗した場合にエラーを投げるかどうか
+
+#### 返り値
+
+`mutate` は `data` パラメータとして扱われる結果を返します。`mutate` に渡された関数は対応するキャッシュの値としても使われる更新後のデータを返します。
+もし、関数の実行中にエラーが発生した場合、そのエラーはスローされるので適切に処理できます。
+
+```jsx
+try {
+ const user = await mutate('/api/user', updateUser(newUser))
+} catch (error) {
+ // ユーザーの更新中に発生したエラーを処理します
+}
+```
+
+## `useSWRMutation`
+
+加えて SWR はリモートミューテーションのために `useSWRMutation` というフックも提供しています。このリモートミューテーションは、自動的にミューテーションを行う `useSWR` などとは異なり手動でのみ発行されます。
+
+また、このフックは他の `useSWRMutation` と状態を共有しません。
+
+```jsx
+import useSWRMutation from 'swr/mutation'
+
+// フェッチャーの実装
+// 追加の引数は第二引数の `arg` プロパティとして渡されます
+// 下記の例では、`arg` は `'my_token'` となります
+async function updateUser(url, { arg }) {
+ await fetch(url, {
+ method: 'POST',
+ headers: {
+ Authorization: `Bearer ${arg}`
+ }
+ })
+}
+
+function Profile() {
+ // useSWR と mutate を組み合わせたような API ですが、リクエストを自動的に開始しません
+ const { trigger } = useSWRMutation('/api/user', updateUser, options?)
+
+ return {
+ // `updateUser` を指定した引数と一緒に実行します
+ trigger('my_token')
+ }}>Update User
+}
+```
+
+### API
+
+#### パラメータ
+
+- `key`: [`mutate`](/docs/mutation#mutate) の `key` と同様
+- `fetcher(key, { arg })`: リモートミューテーションのための非同期関数
+- `options`: 下記のプロパティを持つオプションのオブジェクト
+ - `optimisticData`: `mutate` の `optimisticData` と同様
+ - `revalidate = true`: `mutate` の `revalidate` と同様
+ - `populateCache = false`: `mutate` の `populateCache` と同様、ただしデフォルト値は `false`
+ - `rollbackOnError = true`: `mutate` の `rollbackOnError` と同様
+ - `throwOnError = true`: `mutate` の `throwOnError` と同様
+ - `onSuccess(data, key, config)`: リモートミューテーションが成功した場合に呼ばれるコールバック関数
+ - `onError(err, key, config)`: リモートミューテーションがエラーになった場合に呼ばれるコールバック関数
+
+#### 返り値
+
+- `data`: `fetcher` から返された渡されたキーに対応するデータ
+- `error`: `fetcher` で発生したエラー (または undefined)
+- `trigger(arg, options)`: リモートミューテーションを発行するための関数
+- `reset`: 状態 (`data`, `error`, `isMutating`) をリセットするための関数
+- `isMutating`: 実行中のリモートミューテーションがあるかどうか
+
+### 基本的な使い方
+
+```jsx
+import useSWRMutation from 'swr/mutation'
+
+async function sendRequest(url, { arg }) {
+ return fetch(url, {
+ method: 'POST',
+ body: JSON.stringify(arg)
+ })
+}
+
+function App() {
+ const { trigger, isMutating } = useSWRMutation('/api/user', sendRequest, /* options */)
+
+ return (
+ {
+ try {
+ const result = await trigger({ username: 'johndoe' }, /* options */)
+ } catch (e) {
+ // エラーハンドリング
+ }
+ }}
+ >
+ Create User
+
+ )
+}
+```
+
+もし、ミューテーションの結果をレンダリングで使いたい場合、`useSWRMutation` の返り値から取得できます。
+
+```jsx
+const { trigger, data, error } = useSWRMutation('/api/user', sendRequest)
+```
+
+`useSWRMutation` はキャッシュを `useSWR` と共有します。そのため、`useSWR` とのレースコンディションを検出し回避できます。また、楽観的更新やエラー時のロールバックのような `mutate` が持っている機能もサポートしています。これらのオプションは `useSWRMutation` と `trigger` 関数に渡すことができます。
+
+```jsx
+const { trigger } = useSWRMutation('/api/user', updateUser, {
+ optimisticData: current => ({ ...current, name: newName })
+})
+
+// または
+
+trigger(newName, {
+ optimisticData: current => ({ ...current, name: newName })
+})
+```
+
+### 必要になるまでデータの読み込みを遅延させる
+
+`useSWRMutation` をデータのローディングのために使うこともできます。`useSWRMutation` は `trigger` が呼ばれるまでリクエストを開始しません。そのため、データが実際に必要になるまでデータの読み込みを遅延することができます。
+
+```jsx
+import { useState } from 'react'
+import useSWRMutation from 'swr/mutation'
+
+const fetcher = url => fetch(url).then(res => res.json())
+
+const Page = () => {
+ const [show, setShow] = useState(false)
+ // trigger が呼ばれるまで data は undefined
+ const { data: user, trigger } = useSWRMutation('/api/user', fetcher);
+
+ return (
+
+
{
+ trigger();
+ setShow(true);
+ }}>Show User
+ {show && user ?
{usre.name}
: null}
+
+ );
+}
+```
-## 楽観的な更新
+## 楽観的更新
-多くの場合、データにローカルミューテーションを適用することは、変更をより速く
-感じさせるための良い方法です。データのリモートソースを待つ必要はありません。
+多くの場合、ローカルミューテーションをデータに対して適用することは、変更を速く感じさせるための良い方法です。
+リモートデータが更新されるまで待つ必要はありません。
-`mutate` を使用すると、再検証の間にプログラムでローカルデータを更新しておき、
-最終的に最新のデータに置き換えることができます。
+`optimisticData` オプションを使うことで、リモートミューテーションの完了を待っている間にローカルデータを手動で更新できます。
+また、`rollbackOnError` オプションを組み合わせることで、データのロールバックも制御できます。
```jsx
import useSWR, { useSWRConfig } from 'swr'
@@ -65,11 +274,17 @@ function Profile () {
{
const newName = data.name.toUpperCase()
const user = { ...data, name: newName }
- const options = { optimisticData: user, rollbackOnError: true }
-
- // 再検証をせずに直ちにローカルデータを更新します
- // ソースを更新するためにリクエストを送信します
- // ローカルデータが最新であることを確かめるために再検証(再取得)を起動します
+ const options = {
+ optimisticData: user,
+ rollbackOnError(error) {
+ // タイムアウトの AbortError だった場合はロールバックしません
+ return error.name !== 'AbortError'
+ },
+ }
+
+ // ローカルのデータを即座に更新します
+ // データを更新するためにリクエストを送信します
+ // ローカルデータが正しいことを保証するために再検証 (再フェッチ) を発行します
mutate('/api/user', updateFn(user), options);
}}>Uppercase my name!
@@ -77,32 +292,67 @@ function Profile () {
}
```
-> **`updateFn`** は、リモートミューテーションを処理するための promise 関数か非同期関数でなければならず、更新されたデータを返す必要があります。
+> **`updateFn`** はリモートミューテーションを処理するための Promise か 非同期関数であり、更新後のデータを返します
-## 現在のデータにもとづいたミューテート
+現在のデータに応じて処理したい場合、`optimisticData` には関数を渡すことも可能です。
-現在のデータにもとづいて、データの一部を更新したい場合があります。
+```jsx
+import useSWR, { useSWRConfig } from 'swr'
-`mutate` を使用すると、現在キャッシュされている値がある場合はそれを受け取り、更新されたドキュメントを返す非同期関数を渡すことができます。
+function Profile () {
+ const { mutate } = useSWRConfig()
+ const { data } = useSWR('/api/user', fetcher)
+
+ return (
+
+
My name is {data.name}.
+ {
+ const newName = data.name.toUpperCase()
+ mutate('/api/user', updateUserName(newName), {
+ optimisticData: user => ({ ...user, name: newName }),
+ rollbackOnError: true
+ });
+ }}>Uppercase my name!
+
+ )
+}
+```
+
+同様のことは `useSWRMutation` と `trigger` でも可能です。
```jsx
-mutate('/api/todos', async todos => {
- // 完了するために ID `1` で todo を更新しましょう。
- // この API は更新されたデータを返します。
- const updatedTodo = await fetch('/api/todos/1', {
- method: 'PATCH',
- body: JSON.stringify({ completed: true })
- })
+import useSWRMutation from 'swr/mutation'
- // リストをフィルタリングし、更新されたアイテムを返します
- const filteredTodos = todos.filter(todo => todo.id !== '1')
- return [...filteredTodos, updatedTodo]
-// API からすでに更新後の情報が取得できるため
-// 再検証する必要はありません
-}, { revalidate: false })
+function Profile () {
+ const { trigger } = useSWRMutation('/api/user', updateUserName)
+
+ return (
+
+
My name is {data.name}.
+ {
+ const newName = data.name.toUpperCase()
+
+ trigger(newName, {
+ optimisticData: user => ({ ...user, name: newName }),
+ rollbackOnError: true
+ })
+ }}>Uppercase my name!
+
+ )
+}
```
-`populateCache` オプションも使用可能です。
+## エラー時のロールバック
+
+`optimisticData` を持っている場合でリモートミューテーションが失敗した場合、
+楽観的な更新によるデータがユーザーに表示されます。
+この場合、ユーザーが正しいデータを見ていることを保証するために、
+`rollbackOnError` を有効にしてローカルキャッシュに対する変更を取り消して以前のデータに戻すことができます。
+
+## ミューテーション後にキャッシュを更新する
+
+リモートミューテーションのリクエストが更新後のデータを直接返す場合もあります。この場合、データをロードするための余計なフェッチが必要ありません。
+`populateCache` オプションを有効にすることで、ミューテーションのレスポンスで `useSWR` のキャッシュを更新できます!
```jsx
const updateTodo = () => fetch('/api/todos/1', {
@@ -112,54 +362,130 @@ const updateTodo = () => fetch('/api/todos/1', {
mutate('/api/todos', updateTodo, {
populateCache: (updatedTodo, todos) => {
- // リストをフィルタリングし、更新されたアイテムを返します
+ // リストをフィルタリングして更新後のアイテムと一緒に返します
+ const filteredTodos = todos.filter(todo => todo.id !== '1')
+ return [...filteredTodos, updatedTodo]
+ },
+ // API が更新後の情報を返してくれているので、
+ // 再検証は必要ありません
+ revalidate: false
+})
+```
+
+`useSWRMutation` フックでも使えます。
+
+```jsx
+useSWRMutation('/api/todos', updateTodo, {
+ populateCache: (updatedTodo, todos) => {
+ // リストをフィルタリングして更新後のアイテムと一緒に返します
const filteredTodos = todos.filter(todo => todo.id !== '1')
return [...filteredTodos, updatedTodo]
},
- // API からすでに更新後の情報が取得できるため
- // 再検証する必要はありません
+ // API が更新後の情報を返してくれているので、
+ // 再検証は必要ありません
revalidate: false
})
```
-## ミューテートから返されたデータ
+`optimisticData` と `rollbackOnError` を組み合わせることで、完璧な楽観的 UI 更新の体験を得ることができます。
-ほとんどの場合、キャッシュを更新するためにいくつかのデータが必要です。データは、`mutate` に渡された promise や非同期関数から解決または返されます。
+## レースコンディションを避ける
-`mutate` に渡されるこの関数は、対応するキャッシュ値を更新できるように、更新されたドキュメントを返します。この関数を実行している際にエラーが発生したときには、適切な対処ができるようにそのエラーを投げます。
+`mutate` と `useSWRMutation` は `useSWR` とのレースコンディションを避けることができます。例えば、
-```jsx
-try {
- const user = await mutate('/api/user', updateUser(newUser))
-} catch (error) {
- // ここでユーザーの更新中にエラーを処理します
+```tsx
+function Profile() {
+ const { data } = useSWR('/api/user', getUser, { revalidateInterval: 3000 })
+ const { trigger } = useSWRMutation('/api/user', updateUser)
+
+ return <>
+ {data ? data.username : null}
+ trigger()}>Update User
+ >
}
```
-## バウンドミューテート
+通常の `useSWR` フックはフォーカスやポーリング、その他の条件によりいつでもそのデータを再検証します。
+これは表示されているユーザー名を可能な限り最新に保ちます。しかしながら、ミューテーションが `useSWR` の再フェッチとほぼ同時に発生する可能性もあり、
+`getUser` が先に開始したものの `updateUser` より時間がかかるようなケースでレースコンディションが発生します。
+
+幸いにも、`useSWRMutation` はこれを自動的に処理してくれます。ミューテーションの後、`useSWR` に実行中のリクエストを再検証を破棄するように伝えます。
+その結果、古いデータは決して表示されません。
+
+## 現在のデータをもとにミューテートする
-`useSWR` によって返される SWR オブジェクトには、SWR のキーに事前にバインドされている `mutate()` 関数も含まれています。
+現在のデータに基づいてデータを更新したい場合もあります。
-機能的にはグローバルな `mutate` 関数と同等ですが、`key` パラメーターは必要ありません。
+`mutate` では、現在のデータを受け取り更新後のデータを返す非同期関数を渡すことができます。
```jsx
-import useSWR from 'swr'
+mutate('/api/todos', async todos => {
+ // ID が `1` の TODO を完了に更新しましょう
+ // この API は更新後のデータを返します
+ const updatedTodo = await fetch('/api/todos/1', {
+ method: 'PATCH',
+ body: JSON.stringify({ completed: true })
+ })
-function Profile () {
- const { data, mutate } = useSWR('/api/user', fetcher)
+ // リストをフィルタリングして、更新後のデータを返します
+ const filteredTodos = todos.filter(todo => todo.id !== '1')
+ return [...filteredTodos, updatedTodo]
+// API が更新後の情報を返してくれているので、
+// 再検証は必要ありません
+}, { revalidate: false })
+```
- return (
-
-
My name is {data.name}.
- {
- const newName = data.name.toUpperCase()
- // このデータを更新するために API へリクエストを送ります
- await requestUpdateUsername(newName)
- // ローカルデータをすぐに更新し、再検証(再フェッチ)します
- // 注:useSWR の mutate を事前にバインドされているものとして使用する場合にはキーは必要ありません
- mutate({ ...data, name: newName })
- }}>Uppercase my name!
-
- )
-}
+## 複数のアイテムをミューテートする
+
+グローバルの `mutate` API はフィルタ関数を受け取ります。この関数は `key` を引数として受け取り再検証するかどうかを返します。フィルタ関数は全ての存在するキャッシュキーに対して適用されます。
+
+```jsx
+import { mutate } from 'swr'
+// キャッシュプロバイダをカスタマイズしている場合はフックから
+// { mutate } = useSWRConfig()
+
+mutate(
+ key => typeof key === 'string' && key.startsWith('/api/item?id='),
+ undefined,
+ { revalidate: true }
+)
+```
+
+これは、配列のようなどんなキーの種類に対しても動作します。下記の例では、最初の要素が `'item'` である全てのキーに対してマッチします。
+
+```jsx
+useSWR(['item', 123], ...)
+useSWR(['item', 124], ...)
+useSWR(['item', 125], ...)
+
+mutate(
+ key => Array.isArray(key) && key[0] === 'item',
+ undefined,
+ { revalidate: false }
+)
+```
+
+フィルタ関数は全ての存在するキャッシュキーに対して適用されます。そのため複数の形式のキーを扱っている場合、キャッシュの形式に対して思い込みを持ってはいけません。
+
+```jsx
+// ✅ 配列のキーに対してマッチします
+mutate((key) => key[0].startsWith('/api'), data)
+// ✅ 文字列のキーに対してマッチします
+mutate((key) => typeof key === 'string' && key.startsWith('/api'), data)
+
+// ❌ エラー: 不確かなキー (配列または文字列) に対してミューテートします
+mutate((key: any) => /\/api/.test(key.toString()))
+```
+
+フィルタ関数を全てのキャッシュデータを消去するためにも使えます、これはログアウトした場合などに便利です。
+
+```js
+const clearCache = () => mutate(
+ () => true,
+ undefined,
+ { revalidate: false }
+)
+
+// ...ログアウト時にキャッシュをクリアします
+clearCache()
```
diff --git a/pages/docs/mutation.ko.md b/pages/docs/mutation.ko.md
index f74d691c..da55af03 100644
--- a/pages/docs/mutation.ko.md
+++ b/pages/docs/mutation.ko.md
@@ -1,25 +1,69 @@
-# 뮤테이션
+# Mutation & Revalidation
+
+SWR provides the [`mutate`](/docs/mutation#mutate) and [`useSWRMutation`](/docs/mutation#useswrmutation) APIs for mutating remote data and related cache.
+
+## `mutate`
+
+There're 2 ways to use the `mutate` API to mutate the data, the global mutate API which can mutate any key and the bound mutate API which only can mutate the data of corresponding SWR hook.
+
+#### Global Mutate
+
+The recommended way to get the global mutator is to use the [`useSWRConfig`](/docs/global-configuration#access-to-global-configurations) hook:
```js
-mutate(key, data, options)
+import { useSWRConfig } from "swr"
+
+function App() {
+ const { mutate } = useSWRConfig()
+ mutate(key, data, options)
+}
```
-## Options
+You can also import it globally:
+
+```js
+import { mutate } from "swr"
-- `optimisticData`: 클라이언트 캐시를 즉시 업데이트하기 위한 데이터. 일반적으로 낙관적인 UI에서 사용됩니다.
-- `revalidate`: 비동기 업데이트가 해소되면 캐시를 갱신합니다.
-- `populateCache`: should the result of the remote mutation be written to the cache, or a function that receives new result and current result as arguments and returns the mutation result.
-- `rollbackOnError`: 원격 뮤테이션 에러 시 캐시를 롤백합니다.
+function App() {
+ mutate(key, data, options)
+}
+```
-## 갱신하기
+#### Bound Mutate
-`useSWRConfig()` hook으로부터 `mutate` 함수를 얻을 수 있으며, `mutate(key)`를 호출하여
-동일한 키를 사용하는 다른 SWR hook* 에게 갱신 메시지를 전역으로 브로드캐스팅할 수 있습니다.
+Bound mutate is the short path to mutate the current key with data. Which `key` is bounded to the `key` passing to `useSWR`, and receive the `data` as the first argument.
-이 예시는 유저가 “로그아웃” 버튼을 클릭할 때 로그인 정보(예, ` ` 내부)를 자동으로 갱신하는
-방법을 보여줍니다.
+It is functionally equivalent to the global `mutate` function in the previous section but does not require the `key` parameter:
```jsx
+import useSWR from 'swr'
+
+function Profile () {
+ const { data, mutate } = useSWR('/api/user', fetcher)
+
+ return (
+
+
My name is {data.name}.
+ {
+ const newName = data.name.toUpperCase()
+ // send a request to the API to update the data
+ await requestUpdateUsername(newName)
+ // update the local data immediately and revalidate (refetch)
+ // NOTE: key is not required when using useSWR's mutate as it's pre-bound
+ mutate({ ...data, name: newName })
+ }}>Uppercase my name!
+
+ )
+}
+```
+
+#### Revalidation
+
+When you call `mutate(key)` (or just `mutate()` with the bound mutate API) without any data, it will trigger a revalidation (mark the data as expired and trigger a refetch)
+for the resource. This example shows how to automatically refetch the login info (e.g. inside ` `)
+when the user clicks the “Logout” button:
+
+```jsx {14}
import useSWR, { useSWRConfig } from 'swr'
function App () {
@@ -29,10 +73,10 @@ function App () {
{
- // 쿠키를 만료된 것으로 설정
+ // set the cookie as expired
document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'
- // 이 키로 모든 SWR에게 갱신하도록 요청
+ // tell all SWRs with this key to revalidate
mutate('/api/user')
}}>
Logout
@@ -42,15 +86,180 @@ function App () {
}
```
-*: _동일한 [캐시 공급자](/docs/advanced/cache) 범위 아래의 SWR hook에게 브로드캐스팅합니다. 캐시 공급자가 존재하지 않을 경우, 모든 SWR hook으로 브로드캐스팅합니다._
+import Callout from 'nextra-theme-docs/callout'
+
+
+It broadcasts to SWR hooks under the same [cache provider](/docs/advanced/cache) scope. If no cache provider exists, it will broadcast to all SWR hooks.
+
+
+
+### API
+
+#### Parameters
+
+- `key`: same as `useSWR`'s `key`, but a function behaves as [a filter function](/docs/mutation#mutate-multiple-items)
+- `data`: data to update the client cache, or an async function for the remote mutation
+- `options`: accepts the following options
+ - `optimisticData`: data to immediately update the client cache, or a function that receives current data and returns the new client cache data, usually used in optimistic UI.
+ - `revalidate = true`: should the cache revalidate once the asynchronous update resolves.
+ - `populateCache = true`: should the result of the remote mutation be written to the cache, or a function that receives new result and current result as arguments and returns the mutation result.
+ - `rollbackOnError = true`: should the cache rollback if the remote mutation errors, or a function that receives the error thrown from fetcher as arguments and returns a boolean whether should rollback or not.
+ - `throwOnError = true`: should the mutate call throw the error when fails.
+
+#### Return Values
+
+`mutate` returns the results the `data` parameter has been resolved. The function passed to `mutate` will return an updated data which is used to update the corresponding cache value. If there is an error thrown while executing the function, the error will be thrown so it can be handled appropriately.
+
+```jsx
+try {
+ const user = await mutate('/api/user', updateUser(newUser))
+} catch (error) {
+ // Handle an error while updating the user here
+}
+```
+
+## `useSWRMutation`
+
+SWR also provides `useSWRMutation` as a hook for remote mutations. The remote mutations are only triggered manually, instead of automatically like `useSWR`.
+
+Also, this hook doesn’t share states with other `useSWRMutation` hooks.
+
+```jsx
+import useSWRMutation from 'swr/mutation'
+
+// Fetcher implementation.
+// The extra argument will be passed via the `arg` property of the 2nd parameter.
+// In the example below, `arg` will be `'my_token'`
+async function updateUser(url, { arg }) {
+ await fetch(url, {
+ method: 'POST',
+ headers: {
+ Authorization: `Bearer ${arg}`
+ }
+ })
+}
+
+function Profile() {
+ // A useSWR + mutate like API, but it will not start the request automatically.
+ const { trigger } = useSWRMutation('/api/user', updateUser, options?)
+
+ return {
+ // Trigger `updateUser` with a specific argument.
+ trigger('my_token')
+ }}>Update User
+}
+```
+
+### API
+
+#### Parameters
+
+- `key`: same as [`mutate`](/docs/mutation#mutate)'s `key`
+- `fetcher(key, { arg })`: an async function for remote mutation
+- `options`: an optional object with the following properties:
+ - `optimisticData`: same as `mutate`'s `optimisticData`
+ - `revalidate = true`: same as `mutate`'s `revalidate`
+ - `populateCache = false`: same as `mutate`'s `populateCache`, but the default is `false`
+ - `rollbackOnError = true`: same as `mutate`'s `rollbackOnError`
+ - `throwOnError = true`: same as `mutate`'s `throwOnError`
+ - `onSuccess(data, key, config)`: callback function when a remote mutation has been finished successfully
+ - `onError(err, key, config)`: callback function when a remote mutation has returned an error
+
+#### Return Values
+
+- `data`: data for the given key returned from `fetcher`
+- `error`: error thrown by `fetcher` (or undefined)
+- `trigger(arg, options)`: a function to trigger a remote mutation
+- `reset`: a function to reset the state (`data`, `error`, `isMutating`)
+- `isMutating`: if there's an ongoing remote mutation
+
+### Basic Usage
+
+```jsx
+import useSWRMutation from 'swr/mutation'
+
+async function sendRequest(url, { arg }) {
+ return fetch(url, {
+ method: 'POST',
+ body: JSON.stringify(arg)
+ })
+}
+
+function App() {
+ const { trigger, isMutating } = useSWRMutation('/api/user', sendRequest, /* options */)
+
+ return (
+ {
+ try {
+ const result = await trigger({ username: 'johndoe' }, /* options */)
+ } catch (e) {
+ // error handling
+ }
+ }}
+ >
+ Create User
+
+ )
+}
+```
+
+If you want to use the mutation results in rendering, you can get them from the return values of `useSWRMutation`.
-## 낙관적인 업데이트
+```jsx
+const { trigger, data, error } = useSWRMutation('/api/user', sendRequest)
+```
-많은 경우에, 데이터에 로컬 뮤테이션을 적용하는 것은 변경을 더 빠르게 느낄 수 있게 해주는 좋은 방법입니다.
-데이터의 원격 소스를 기다릴 필요가 없습니다.
+`useSWRMutation` shares a cache store with `useSWR`, so it can detect and avoid race conditions between `useSWR`. It also supports `mutate`'s functionalities like optimistic updates and rollback on errors. You can pass these options `useSWRMutation` and its `trigger` function.
-`mutate`를 사용하면 데이터를 재검증하고 최종적으로 최신 데이터로 대체하는 동안에,
-로컬 데이터를 프로그래밍 방식으로 업데이트할 수 있습니다.
+```jsx
+const { trigger } = useSWRMutation('/api/user', updateUser, {
+ optimisticData: current => ({ ...current, name: newName })
+})
+
+// or
+
+trigger(newName, {
+ optimisticData: current => ({ ...current, name: newName })
+})
+```
+
+### Defer loading data until needed
+
+You can also use `useSWRMutation` for loading data. `useSWRMutation` never start requesting until `trigger` is called, so you can defer loading data when you actually need it.
+
+```jsx
+import { useState } from 'react'
+import useSWRMutation from 'swr/mutation'
+
+const fetcher = url => fetch(url).then(res => res.json())
+
+const Page = () => {
+ const [show, setShow] = useState(false)
+ // data is undefined until trigger is called
+ const { data: user, trigger } = useSWRMutation('/api/user', fetcher);
+
+ return (
+
+
{
+ trigger();
+ setShow(true);
+ }}>Show User
+ {show && user ?
{usre.name}
: null}
+
+ );
+}
+```
+
+## Optimistic Updates
+
+In many cases, applying local mutations to data is a good way to make changes
+feel faster — no need to wait for the remote source of data.
+
+With the `optimisticData` option, you can update your local data manually, while
+waiting for the remote mutation to finish. Composing `rollbackOnError` you can also
+control when to rollback the data.
```jsx
import useSWR, { useSWRConfig } from 'swr'
@@ -65,11 +274,17 @@ function Profile () {
{
const newName = data.name.toUpperCase()
const user = { ...data, name: newName }
- const options = { optimisticData: user, rollbackOnError: true }
-
- // 로컬 데이터를 즉시 업데이트하고
- // 데이터 업데이트 요청을 전송하고
- // 로컬 데이터가 올바른지 확인하기 위해 갱신(다시 가져오기)을 트리거합니다.
+ const options = {
+ optimisticData: user,
+ rollbackOnError(error) {
+ // If it's timeout abort error, don't rollback
+ return error.name !== 'AbortError'
+ },
+ }
+
+ // updates the local data immediately
+ // send a request to update the data
+ // triggers a revalidation (refetch) to make sure our local data is correct
mutate('/api/user', updateFn(user), options);
}}>Uppercase my name!
@@ -77,32 +292,67 @@ function Profile () {
}
```
-> **`updateFn`**은 원격 뮤테이션을 다루기 위한 promise 또는 비동기 함수여야 합니다. 업데이트한 데이터를 반환해야 합니다.
+> The **`updateFn`** should be a promise or asynchronous function to handle the remote mutation, it should return updated data.
+
+You can also pass a function to `optimisticData` to make it depending on the current data:
+
+```jsx
+import useSWR, { useSWRConfig } from 'swr'
-## 현재 데이터를 기반으로 뮤테이트
+function Profile () {
+ const { mutate } = useSWRConfig()
+ const { data } = useSWR('/api/user', fetcher)
-현재 데이터를 기반으로 데이터의 일부를 업데이트하려는 경우가 있습니다.
+ return (
+
+
My name is {data.name}.
+ {
+ const newName = data.name.toUpperCase()
+ mutate('/api/user', updateUserName(newName), {
+ optimisticData: user => ({ ...user, name: newName }),
+ rollbackOnError: true
+ });
+ }}>Uppercase my name!
+
+ )
+}
+```
-`mutate`를 사용해 현재 캐시 된 값을 받는(있으면 업데이트된 문서를 반환) 비동기 함수를 전달할 수 있습니다.
+You can also create the same thing with `useSWRMutation` and `trigger`:
```jsx
-mutate('/api/todos', async todos => {
- // ID `1`을 갖는 todo를 업데이트해 완료되도록 해봅시다
- // 이 API는 업데이트된 데이터를 반환합니다
- const updatedTodo = await fetch('/api/todos/1', {
- method: 'PATCH',
- body: JSON.stringify({ completed: true })
- })
+import useSWRMutation from 'swr/mutation'
- // 리스트를 필터링하고 업데이트된 항목을 반환합니다
- const filteredTodos = todos.filter(todo => todo.id !== '1')
- return [...filteredTodos, updatedTodo]
-// Since the API already gives us the updated information,
-// we don't need to revalidate here.
-}, { revalidate: false })
+function Profile () {
+ const { trigger } = useSWRMutation('/api/user', updateUserName)
+
+ return (
+
+
My name is {data.name}.
+ {
+ const newName = data.name.toUpperCase()
+
+ trigger(newName, {
+ optimisticData: user => ({ ...user, name: newName }),
+ rollbackOnError: true
+ })
+ }}>Uppercase my name!
+
+ )
+}
```
-You can also use the `populateCache` option.
+## Rollback on Errors
+
+When you have `optimisticData` set, it’s possible that the optimistic data gets
+displayed to the user, but the remote mutation fails. In this case, you can leverage
+`rollbackOnError` to revert the local cache to the previous state, to make sure
+the user is seeing the correct data.
+
+## Update Cache After Mutation
+
+Sometimes, the remote mutation request directly returns the updated data, so there is no need to do an extra fetch to load it.
+You can enable the `populateCache` option to update the cache for `useSWR` with the response of the mutation:
```jsx
const updateTodo = () => fetch('/api/todos/1', {
@@ -122,44 +372,120 @@ mutate('/api/todos', updateTodo, {
})
```
-## 뮤테이트로부터 반환된 데이터
+Or with the `useSWRMutation` hook:
+
+```jsx
+useSWRMutation('/api/todos', updateTodo, {
+ populateCache: (updatedTodo, todos) => {
+ // filter the list, and return it with the updated item
+ const filteredTodos = todos.filter(todo => todo.id !== '1')
+ return [...filteredTodos, updatedTodo]
+ },
+ // Since the API already gives us the updated information,
+ // we don't need to revalidate here.
+ revalidate: false
+})
+```
+
+When combined with `optimisticData` and `rollbackOnError`, you’ll get a perfect optimistic UI experience.
-여러분 대부분은 아마도 캐시를 업데이트하기 위한 어떤 데이터가 필요할 것입니다. 데이터는 `mutate`로 전달했던 프로미스나 비동기 함수로부터 이행되었거나 반환되었습니다.
+## Avoid Race Conditions
-`mutate`로 전달된 함수는 적합한 캐시 값을 업데이트하기 위해 사용되는 업데이트된 문서를 반환합니다. 해당 함수를 실행하는 동안 에러가 발생한다면,
-적절하게 처리할 수 있도록 에러가 던져집니다.
+Both `mutate` and `useSWRMutation` can avoid race conditions between `useSWR`. For example,
-```jsx
-try {
- const user = await mutate('/api/user', updateUser(newUser))
-} catch (error) {
- // 여기에서 user를 업데이트하는 동안 발생하는 에러를 처리
+```tsx
+function Profile() {
+ const { data } = useSWR('/api/user', getUser, { revalidateInterval: 3000 })
+ const { trigger } = useSWRMutation('/api/user', updateUser)
+
+ return <>
+ {data ? data.username : null}
+ trigger()}>Update User
+ >
}
```
-## 바인딩 된 뮤테이트
+The normal `useSWR` hook might refresh its data any time due to focus, polling, or other conditions. This way the displayed username
+can be as fresh as possible. However, since we have a mutation there that can happen at the nearly same time of a refetch of `useSWR`, there
+could be a race condition that `getUser` request starts earlier, but takes longer than `updateUser`.
-`useSWR`에 의해 반환된 SWR 객체는 SWR의 키로 미리 바인딩 된 `mutate()` 함수도 포함합니다.
-기능적으로는 전역 `mutate` 함수와 동일하지만 `key` 파라미터를 요구하지 않습니다.
+Luckily, `useSWRMutation` handles this for you automatically. After the mutation, it will tell `useSWR` to ditch the ongoing request and revalidate,
+so the stale data will never be displayed.
+
+## Mutate Based on Current Data
+
+Sometimes, you want to update a part of your data based on the current data.
+
+With `mutate`, you can pass an async function which will receive the current cached value, if any, and returns an updated document.
```jsx
-import useSWR from 'swr'
+mutate('/api/todos', async todos => {
+ // let's update the todo with ID `1` to be completed,
+ // this API returns the updated data
+ const updatedTodo = await fetch('/api/todos/1', {
+ method: 'PATCH',
+ body: JSON.stringify({ completed: true })
+ })
-function Profile () {
- const { data, mutate } = useSWR('/api/user', fetcher)
+ // filter the list, and return it with the updated item
+ const filteredTodos = todos.filter(todo => todo.id !== '1')
+ return [...filteredTodos, updatedTodo]
+// Since the API already gives us the updated information,
+// we don't need to revalidate here.
+}, { revalidate: false })
+```
- return (
-
-
My name is {data.name}.
- {
- const newName = data.name.toUpperCase()
- // 데이터를 업데이트하기 위해 API로 요청을 전송
- await requestUpdateUsername(newName)
- // 로컬 데이터를 즉시 업데이트하고 갱신(refetch)
- // 노트: 미리 바인딩 되었으므로 useSWR의 뮤테이트를 사용할 때는 key가 요구되지 않음
- mutate({ ...data, name: newName })
- }}>Uppercase my name!
-
- )
-}
+## Mutate Multiple Items
+
+The global `mutate` API accepts a filter function, which accepts `key` as the argument and returns which keys to revalidate. The filter function is applied to all the existing cache keys:
+
+```jsx
+import { mutate } from 'swr'
+// Or from the hook if you customized the cache provider:
+// { mutate } = useSWRConfig()
+
+mutate(
+ key => typeof key === 'string' && key.startsWith('/api/item?id='),
+ undefined,
+ { revalidate: true }
+)
+```
+
+This also works with any key type like an array. The mutation matches all keys, of which the first element is `'item'`.
+
+```jsx
+useSWR(['item', 123], ...)
+useSWR(['item', 124], ...)
+useSWR(['item', 125], ...)
+
+mutate(
+ key => Array.isArray(key) && key[0] === 'item',
+ undefined,
+ { revalidate: false }
+)
+```
+
+The filter function is applied to all existing cache keys, so you should not assume the shape of keys when using multiple shapes of keys.
+
+```jsx
+// ✅ matching array key
+mutate((key) => key[0].startsWith('/api'), data)
+// ✅ matching string key
+mutate((key) => typeof key === 'string' && key.startsWith('/api'), data)
+
+// ❌ ERROR: mutate uncertain keys (array or string)
+mutate((key: any) => /\/api/.test(key.toString()))
+```
+
+You can use the filter function to clear all cache data, which is useful when logging out:
+
+```js
+const clearCache = () => mutate(
+ () => true,
+ undefined,
+ { revalidate: false }
+)
+
+// ...clear cache on logout
+clearCache()
```
diff --git a/pages/docs/mutation.pt-BR.md b/pages/docs/mutation.pt-BR.md
index ad1fce04..678cb505 100644
--- a/pages/docs/mutation.pt-BR.md
+++ b/pages/docs/mutation.pt-BR.md
@@ -1,24 +1,69 @@
-# Mutação
+# Mutation & Revalidation
+
+SWR provides the [`mutate`](/docs/mutation#mutate) and [`useSWRMutation`](/docs/mutation#useswrmutation) APIs for mutating remote data and related cache.
+
+## `mutate`
+
+There're 2 ways to use the `mutate` API to mutate the data, the global mutate API which can mutate any key and the bound mutate API which only can mutate the data of corresponding SWR hook.
+
+#### Global Mutate
+
+The recommended way to get the global mutator is to use the [`useSWRConfig`](/docs/global-configuration#access-to-global-configurations) hook:
```js
-mutate(key, data, options)
+import { useSWRConfig } from "swr"
+
+function App() {
+ const { mutate } = useSWRConfig()
+ mutate(key, data, options)
+}
```
-## Opções
+You can also import it globally:
+
+```js
+import { mutate } from "swr"
-- `optimisticData`: dados para serem atualizados imediatamente no cache do cliente, comumente usado para UI otimista.
-- `revalidate`: se o cache deve revalidar quando a atualização assíncrona resolve.
-- `populateCache`: should the result of the remote mutation be written to the cache, or a function that receives new result and current result as arguments and returns the mutation result.
-- `rollbackOnError`: se o cache deve reverter se a mutação remota falhar.
+function App() {
+ mutate(key, data, options)
+}
+```
-## Revalidar
+#### Bound Mutate
-Você pode obter a função `mutate` do hook `useSWRConfig()`, e transmitir uma mensagem de revalidação global
-para outros hooks SWR\* usando a mesma chave chamando `mutate(key)`.
+Bound mutate is the short path to mutate the current key with data. Which `key` is bounded to the `key` passing to `useSWR`, and receive the `data` as the first argument.
-Esse exemplo mostra como recuperar automaticamente as informações de login (ex. dentro de ` `)
+It is functionally equivalent to the global `mutate` function in the previous section but does not require the `key` parameter:
```jsx
+import useSWR from 'swr'
+
+function Profile () {
+ const { data, mutate } = useSWR('/api/user', fetcher)
+
+ return (
+
+
My name is {data.name}.
+ {
+ const newName = data.name.toUpperCase()
+ // send a request to the API to update the data
+ await requestUpdateUsername(newName)
+ // update the local data immediately and revalidate (refetch)
+ // NOTE: key is not required when using useSWR's mutate as it's pre-bound
+ mutate({ ...data, name: newName })
+ }}>Uppercase my name!
+
+ )
+}
+```
+
+#### Revalidation
+
+When you call `mutate(key)` (or just `mutate()` with the bound mutate API) without any data, it will trigger a revalidation (mark the data as expired and trigger a refetch)
+for the resource. This example shows how to automatically refetch the login info (e.g. inside ` `)
+when the user clicks the “Logout” button:
+
+```jsx {14}
import useSWR, { useSWRConfig } from 'swr'
function App () {
@@ -28,10 +73,10 @@ function App () {
{
- // define o cooke como expirado
+ // set the cookie as expired
document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'
- // avisa todos os SWR com essa chave para revalidar
+ // tell all SWRs with this key to revalidate
mutate('/api/user')
}}>
Logout
@@ -41,15 +86,180 @@ function App () {
}
```
-\*: _É transmitido para os hooks SWR no mesmo escopo do [cache provider](/docs/advanced/cache). Se nenhum cache provider existir, será transmitido para todos os hooks SWR._
+import Callout from 'nextra-theme-docs/callout'
+
+
+It broadcasts to SWR hooks under the same [cache provider](/docs/advanced/cache) scope. If no cache provider exists, it will broadcast to all SWR hooks.
+
+
+
+### API
+
+#### Parameters
+
+- `key`: same as `useSWR`'s `key`, but a function behaves as [a filter function](/docs/mutation#mutate-multiple-items)
+- `data`: data to update the client cache, or an async function for the remote mutation
+- `options`: accepts the following options
+ - `optimisticData`: data to immediately update the client cache, or a function that receives current data and returns the new client cache data, usually used in optimistic UI.
+ - `revalidate = true`: should the cache revalidate once the asynchronous update resolves.
+ - `populateCache = true`: should the result of the remote mutation be written to the cache, or a function that receives new result and current result as arguments and returns the mutation result.
+ - `rollbackOnError = true`: should the cache rollback if the remote mutation errors, or a function that receives the error thrown from fetcher as arguments and returns a boolean whether should rollback or not.
+ - `throwOnError = true`: should the mutate call throw the error when fails.
+
+#### Return Values
+
+`mutate` returns the results the `data` parameter has been resolved. The function passed to `mutate` will return an updated data which is used to update the corresponding cache value. If there is an error thrown while executing the function, the error will be thrown so it can be handled appropriately.
+
+```jsx
+try {
+ const user = await mutate('/api/user', updateUser(newUser))
+} catch (error) {
+ // Handle an error while updating the user here
+}
+```
+
+## `useSWRMutation`
+
+SWR also provides `useSWRMutation` as a hook for remote mutations. The remote mutations are only triggered manually, instead of automatically like `useSWR`.
+
+Also, this hook doesn’t share states with other `useSWRMutation` hooks.
+
+```jsx
+import useSWRMutation from 'swr/mutation'
+
+// Fetcher implementation.
+// The extra argument will be passed via the `arg` property of the 2nd parameter.
+// In the example below, `arg` will be `'my_token'`
+async function updateUser(url, { arg }) {
+ await fetch(url, {
+ method: 'POST',
+ headers: {
+ Authorization: `Bearer ${arg}`
+ }
+ })
+}
+
+function Profile() {
+ // A useSWR + mutate like API, but it will not start the request automatically.
+ const { trigger } = useSWRMutation('/api/user', updateUser, options?)
+
+ return {
+ // Trigger `updateUser` with a specific argument.
+ trigger('my_token')
+ }}>Update User
+}
+```
+
+### API
+
+#### Parameters
+
+- `key`: same as [`mutate`](/docs/mutation#mutate)'s `key`
+- `fetcher(key, { arg })`: an async function for remote mutation
+- `options`: an optional object with the following properties:
+ - `optimisticData`: same as `mutate`'s `optimisticData`
+ - `revalidate = true`: same as `mutate`'s `revalidate`
+ - `populateCache = false`: same as `mutate`'s `populateCache`, but the default is `false`
+ - `rollbackOnError = true`: same as `mutate`'s `rollbackOnError`
+ - `throwOnError = true`: same as `mutate`'s `throwOnError`
+ - `onSuccess(data, key, config)`: callback function when a remote mutation has been finished successfully
+ - `onError(err, key, config)`: callback function when a remote mutation has returned an error
+
+#### Return Values
+
+- `data`: data for the given key returned from `fetcher`
+- `error`: error thrown by `fetcher` (or undefined)
+- `trigger(arg, options)`: a function to trigger a remote mutation
+- `reset`: a function to reset the state (`data`, `error`, `isMutating`)
+- `isMutating`: if there's an ongoing remote mutation
+
+### Basic Usage
+
+```jsx
+import useSWRMutation from 'swr/mutation'
+
+async function sendRequest(url, { arg }) {
+ return fetch(url, {
+ method: 'POST',
+ body: JSON.stringify(arg)
+ })
+}
+
+function App() {
+ const { trigger, isMutating } = useSWRMutation('/api/user', sendRequest, /* options */)
+
+ return (
+ {
+ try {
+ const result = await trigger({ username: 'johndoe' }, /* options */)
+ } catch (e) {
+ // error handling
+ }
+ }}
+ >
+ Create User
+
+ )
+}
+```
+
+If you want to use the mutation results in rendering, you can get them from the return values of `useSWRMutation`.
-## Updates Otimistas
+```jsx
+const { trigger, data, error } = useSWRMutation('/api/user', sendRequest)
+```
-Em muitos casos, aplicar mutações locais aos dados é uma boa maneira de fazer alterações
-parecerem mais rápidas — não precisa esperar para a fonte de dados remota.
+`useSWRMutation` shares a cache store with `useSWR`, so it can detect and avoid race conditions between `useSWR`. It also supports `mutate`'s functionalities like optimistic updates and rollback on errors. You can pass these options `useSWRMutation` and its `trigger` function.
-Com `mutate`, você pode atualizar seus dados localmente, enquanto revalidar e
-depois substituir por os dados mais recentes.
+```jsx
+const { trigger } = useSWRMutation('/api/user', updateUser, {
+ optimisticData: current => ({ ...current, name: newName })
+})
+
+// or
+
+trigger(newName, {
+ optimisticData: current => ({ ...current, name: newName })
+})
+```
+
+### Defer loading data until needed
+
+You can also use `useSWRMutation` for loading data. `useSWRMutation` never start requesting until `trigger` is called, so you can defer loading data when you actually need it.
+
+```jsx
+import { useState } from 'react'
+import useSWRMutation from 'swr/mutation'
+
+const fetcher = url => fetch(url).then(res => res.json())
+
+const Page = () => {
+ const [show, setShow] = useState(false)
+ // data is undefined until trigger is called
+ const { data: user, trigger } = useSWRMutation('/api/user', fetcher);
+
+ return (
+
+
{
+ trigger();
+ setShow(true);
+ }}>Show User
+ {show && user ?
{usre.name}
: null}
+
+ );
+}
+```
+
+## Optimistic Updates
+
+In many cases, applying local mutations to data is a good way to make changes
+feel faster — no need to wait for the remote source of data.
+
+With the `optimisticData` option, you can update your local data manually, while
+waiting for the remote mutation to finish. Composing `rollbackOnError` you can also
+control when to rollback the data.
```jsx
import useSWR, { useSWRConfig } from 'swr'
@@ -64,11 +274,17 @@ function Profile () {
{
const newName = data.name.toUpperCase()
const user = { ...data, name: newName }
- const options = { optimisticData: user, rollbackOnError: true }
-
- // atualiza os dados locais imediatamente
- // envia um pedido para atualizar os dados
- // dispara uma revalidação (refetch) para garantir que os nossos dados locais são corretos
+ const options = {
+ optimisticData: user,
+ rollbackOnError(error) {
+ // If it's timeout abort error, don't rollback
+ return error.name !== 'AbortError'
+ },
+ }
+
+ // updates the local data immediately
+ // send a request to update the data
+ // triggers a revalidation (refetch) to make sure our local data is correct
mutate('/api/user', updateFn(user), options);
}}>Uppercase my name!
@@ -76,32 +292,67 @@ function Profile () {
}
```
-> **`updateFn`** deve ser uma promise ou uma função assíncrona para lidar com a mutação remota, e deve retornar os dados atualizados.
+> The **`updateFn`** should be a promise or asynchronous function to handle the remote mutation, it should return updated data.
-## Mutação Baseada nos Dados Atuais
+You can also pass a function to `optimisticData` to make it depending on the current data:
-As vezes, você quer atualizar uma parte de seus dados baseado nos dados atuais.
+```jsx
+import useSWR, { useSWRConfig } from 'swr'
-Com `mutate`, você pode passar uma função assíncrona que recebe o valor atual do cache, se houver, e retorna um documento atualizado.
+function Profile () {
+ const { mutate } = useSWRConfig()
+ const { data } = useSWR('/api/user', fetcher)
+
+ return (
+
+
My name is {data.name}.
+ {
+ const newName = data.name.toUpperCase()
+ mutate('/api/user', updateUserName(newName), {
+ optimisticData: user => ({ ...user, name: newName }),
+ rollbackOnError: true
+ });
+ }}>Uppercase my name!
+
+ )
+}
+```
+
+You can also create the same thing with `useSWRMutation` and `trigger`:
```jsx
-mutate('/api/todos', async todos => {
- // Vamos atualizar o todo com ID `1` para ser completado,
- // essa API retorna os dados atualizados
- const updatedTodo = await fetch('/api/todos/1', {
- method: 'PATCH',
- body: JSON.stringify({ completed: true })
- })
+import useSWRMutation from 'swr/mutation'
- // filtra a lista, e retorna com o item atualizado
- const filteredTodos = todos.filter(todo => todo.id !== '1')
- return [...filteredTodos, updatedTodo]
-// Since the API already gives us the updated information,
-// we don't need to revalidate here.
-}, { revalidate: false })
+function Profile () {
+ const { trigger } = useSWRMutation('/api/user', updateUserName)
+
+ return (
+
+
My name is {data.name}.
+ {
+ const newName = data.name.toUpperCase()
+
+ trigger(newName, {
+ optimisticData: user => ({ ...user, name: newName }),
+ rollbackOnError: true
+ })
+ }}>Uppercase my name!
+
+ )
+}
```
-You can also use the `populateCache` option.
+## Rollback on Errors
+
+When you have `optimisticData` set, it’s possible that the optimistic data gets
+displayed to the user, but the remote mutation fails. In this case, you can leverage
+`rollbackOnError` to revert the local cache to the previous state, to make sure
+the user is seeing the correct data.
+
+## Update Cache After Mutation
+
+Sometimes, the remote mutation request directly returns the updated data, so there is no need to do an extra fetch to load it.
+You can enable the `populateCache` option to update the cache for `useSWR` with the response of the mutation:
```jsx
const updateTodo = () => fetch('/api/todos/1', {
@@ -121,44 +372,120 @@ mutate('/api/todos', updateTodo, {
})
```
-## Data Retornada da Mutação
+Or with the `useSWRMutation` hook:
-Mais provavelmente, você precisa de algum dado para atualizar o cache. O dado é resolvido ou retornado da função ou função assíncrona que você passou para `mutate`.
+```jsx
+useSWRMutation('/api/todos', updateTodo, {
+ populateCache: (updatedTodo, todos) => {
+ // filter the list, and return it with the updated item
+ const filteredTodos = todos.filter(todo => todo.id !== '1')
+ return [...filteredTodos, updatedTodo]
+ },
+ // Since the API already gives us the updated information,
+ // we don't need to revalidate here.
+ revalidate: false
+})
+```
-A função passada para `mutate` retornará um documento atualizado que é usado para atualizar o valor correspondente no cache. Se houver um erro ao executar a função, o erro será lançado para que possa ser tratado adequadamente.
+When combined with `optimisticData` and `rollbackOnError`, you’ll get a perfect optimistic UI experience.
-```jsx
-try {
- const user = await mutate('/api/user', updateUser(newUser))
-} catch (error) {
- // Lide com um erro enquanto atualiza o usuário aqui
+## Avoid Race Conditions
+
+Both `mutate` and `useSWRMutation` can avoid race conditions between `useSWR`. For example,
+
+```tsx
+function Profile() {
+ const { data } = useSWR('/api/user', getUser, { revalidateInterval: 3000 })
+ const { trigger } = useSWRMutation('/api/user', updateUser)
+
+ return <>
+ {data ? data.username : null}
+ trigger()}>Update User
+ >
}
```
-## Mutação Vinculada
+The normal `useSWR` hook might refresh its data any time due to focus, polling, or other conditions. This way the displayed username
+can be as fresh as possible. However, since we have a mutation there that can happen at the nearly same time of a refetch of `useSWR`, there
+could be a race condition that `getUser` request starts earlier, but takes longer than `updateUser`.
-O objeto SWR retornado por `useSWR` também contém uma função `mutate()` que é vinculada ao chave do SWR.
+Luckily, `useSWRMutation` handles this for you automatically. After the mutation, it will tell `useSWR` to ditch the ongoing request and revalidate,
+so the stale data will never be displayed.
-É funcionalmente equivalente à função global `mutate` mas não requer o parâmetro key.
+## Mutate Based on Current Data
+
+Sometimes, you want to update a part of your data based on the current data.
+
+With `mutate`, you can pass an async function which will receive the current cached value, if any, and returns an updated document.
```jsx
-import useSWR from 'swr'
+mutate('/api/todos', async todos => {
+ // let's update the todo with ID `1` to be completed,
+ // this API returns the updated data
+ const updatedTodo = await fetch('/api/todos/1', {
+ method: 'PATCH',
+ body: JSON.stringify({ completed: true })
+ })
-function Profile () {
- const { data, mutate } = useSWR('/api/user', fetcher)
+ // filter the list, and return it with the updated item
+ const filteredTodos = todos.filter(todo => todo.id !== '1')
+ return [...filteredTodos, updatedTodo]
+// Since the API already gives us the updated information,
+// we don't need to revalidate here.
+}, { revalidate: false })
+```
- return (
-
-
My name is {data.name}.
- {
- const newName = data.name.toUpperCase()
- // envia um pedido para a API para atualizar os dados
- await requestUpdateUsername(newName)
- // atualiza os dados locais e revalida (refetch)
- // NOTA: key não é mais necessária quando usando o mutate do useSWR, já que é pré-vinculado
- mutate({ ...data, name: newName })
- }}>Uppercase my name!
-
- )
-}
+## Mutate Multiple Items
+
+The global `mutate` API accepts a filter function, which accepts `key` as the argument and returns which keys to revalidate. The filter function is applied to all the existing cache keys:
+
+```jsx
+import { mutate } from 'swr'
+// Or from the hook if you customized the cache provider:
+// { mutate } = useSWRConfig()
+
+mutate(
+ key => typeof key === 'string' && key.startsWith('/api/item?id='),
+ undefined,
+ { revalidate: true }
+)
+```
+
+This also works with any key type like an array. The mutation matches all keys, of which the first element is `'item'`.
+
+```jsx
+useSWR(['item', 123], ...)
+useSWR(['item', 124], ...)
+useSWR(['item', 125], ...)
+
+mutate(
+ key => Array.isArray(key) && key[0] === 'item',
+ undefined,
+ { revalidate: false }
+)
+```
+
+The filter function is applied to all existing cache keys, so you should not assume the shape of keys when using multiple shapes of keys.
+
+```jsx
+// ✅ matching array key
+mutate((key) => key[0].startsWith('/api'), data)
+// ✅ matching string key
+mutate((key) => typeof key === 'string' && key.startsWith('/api'), data)
+
+// ❌ ERROR: mutate uncertain keys (array or string)
+mutate((key: any) => /\/api/.test(key.toString()))
+```
+
+You can use the filter function to clear all cache data, which is useful when logging out:
+
+```js
+const clearCache = () => mutate(
+ () => true,
+ undefined,
+ { revalidate: false }
+)
+
+// ...clear cache on logout
+clearCache()
```
diff --git a/pages/docs/mutation.ru.md b/pages/docs/mutation.ru.md
index 4663b970..678cb505 100644
--- a/pages/docs/mutation.ru.md
+++ b/pages/docs/mutation.ru.md
@@ -1,23 +1,69 @@
-# Мутация
+# Mutation & Revalidation
+
+SWR provides the [`mutate`](/docs/mutation#mutate) and [`useSWRMutation`](/docs/mutation#useswrmutation) APIs for mutating remote data and related cache.
+
+## `mutate`
+
+There're 2 ways to use the `mutate` API to mutate the data, the global mutate API which can mutate any key and the bound mutate API which only can mutate the data of corresponding SWR hook.
+
+#### Global Mutate
+
+The recommended way to get the global mutator is to use the [`useSWRConfig`](/docs/global-configuration#access-to-global-configurations) hook:
```js
-mutate(key, data, options)
+import { useSWRConfig } from "swr"
+
+function App() {
+ const { mutate } = useSWRConfig()
+ mutate(key, data, options)
+}
```
-## Опции
+You can also import it globally:
+
+```js
+import { mutate } from "swr"
-- `optimisticData`: данные для немедленного обновления кеша клиента, обычно используемые в оптимистичном UI.
-- `revalidate`: должен ли кеш повторно проверяться после разрешения асинхронного обновления.
-- `populateCache`: should the result of the remote mutation be written to the cache, or a function that receives new result and current result as arguments and returns the mutation result.
-- `rollbackOnError`: следует ли выполнять откат кеша в случае ошибок удаленной мутации.
+function App() {
+ mutate(key, data, options)
+}
+```
-## Ревалидация
+#### Bound Mutate
-Вы можете получить функцию `mutate` из хука `useSWRConfig()`, и передать сообщение о ревалидации глобально другим SWR хукам\* , используя тот же ключ, вызвав `mutate(key)`.
+Bound mutate is the short path to mutate the current key with data. Which `key` is bounded to the `key` passing to `useSWR`, and receive the `data` as the first argument.
-В этом примере показано, как автоматически обновлять информацию для входа (например, внутри ` `), когда пользователь нажимает кнопку «Выйти».
+It is functionally equivalent to the global `mutate` function in the previous section but does not require the `key` parameter:
```jsx
+import useSWR from 'swr'
+
+function Profile () {
+ const { data, mutate } = useSWR('/api/user', fetcher)
+
+ return (
+
+
My name is {data.name}.
+ {
+ const newName = data.name.toUpperCase()
+ // send a request to the API to update the data
+ await requestUpdateUsername(newName)
+ // update the local data immediately and revalidate (refetch)
+ // NOTE: key is not required when using useSWR's mutate as it's pre-bound
+ mutate({ ...data, name: newName })
+ }}>Uppercase my name!
+
+ )
+}
+```
+
+#### Revalidation
+
+When you call `mutate(key)` (or just `mutate()` with the bound mutate API) without any data, it will trigger a revalidation (mark the data as expired and trigger a refetch)
+for the resource. This example shows how to automatically refetch the login info (e.g. inside ` `)
+when the user clicks the “Logout” button:
+
+```jsx {14}
import useSWR, { useSWRConfig } from 'swr'
function App () {
@@ -27,26 +73,193 @@ function App () {
{
- // установить cookie как просроченный
+ // set the cookie as expired
document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'
- // сообщить всем SWR с этим ключём, чтобы они ревалидировали
+ // tell all SWRs with this key to revalidate
mutate('/api/user')
}}>
- Выйти
+ Logout
)
}
```
-\*: _Транслируется всем SWR хукам с той же областью действия [кеш провайдера](/docs/cache). Если кеш-провайдера не существует, будет транслироваться на все SWR хуки._
+import Callout from 'nextra-theme-docs/callout'
+
+
+It broadcasts to SWR hooks under the same [cache provider](/docs/advanced/cache) scope. If no cache provider exists, it will broadcast to all SWR hooks.
+
+
+
+### API
+
+#### Parameters
+
+- `key`: same as `useSWR`'s `key`, but a function behaves as [a filter function](/docs/mutation#mutate-multiple-items)
+- `data`: data to update the client cache, or an async function for the remote mutation
+- `options`: accepts the following options
+ - `optimisticData`: data to immediately update the client cache, or a function that receives current data and returns the new client cache data, usually used in optimistic UI.
+ - `revalidate = true`: should the cache revalidate once the asynchronous update resolves.
+ - `populateCache = true`: should the result of the remote mutation be written to the cache, or a function that receives new result and current result as arguments and returns the mutation result.
+ - `rollbackOnError = true`: should the cache rollback if the remote mutation errors, or a function that receives the error thrown from fetcher as arguments and returns a boolean whether should rollback or not.
+ - `throwOnError = true`: should the mutate call throw the error when fails.
+
+#### Return Values
+
+`mutate` returns the results the `data` parameter has been resolved. The function passed to `mutate` will return an updated data which is used to update the corresponding cache value. If there is an error thrown while executing the function, the error will be thrown so it can be handled appropriately.
+
+```jsx
+try {
+ const user = await mutate('/api/user', updateUser(newUser))
+} catch (error) {
+ // Handle an error while updating the user here
+}
+```
+
+## `useSWRMutation`
+
+SWR also provides `useSWRMutation` as a hook for remote mutations. The remote mutations are only triggered manually, instead of automatically like `useSWR`.
+
+Also, this hook doesn’t share states with other `useSWRMutation` hooks.
+
+```jsx
+import useSWRMutation from 'swr/mutation'
+
+// Fetcher implementation.
+// The extra argument will be passed via the `arg` property of the 2nd parameter.
+// In the example below, `arg` will be `'my_token'`
+async function updateUser(url, { arg }) {
+ await fetch(url, {
+ method: 'POST',
+ headers: {
+ Authorization: `Bearer ${arg}`
+ }
+ })
+}
+
+function Profile() {
+ // A useSWR + mutate like API, but it will not start the request automatically.
+ const { trigger } = useSWRMutation('/api/user', updateUser, options?)
+
+ return {
+ // Trigger `updateUser` with a specific argument.
+ trigger('my_token')
+ }}>Update User
+}
+```
+
+### API
+
+#### Parameters
+
+- `key`: same as [`mutate`](/docs/mutation#mutate)'s `key`
+- `fetcher(key, { arg })`: an async function for remote mutation
+- `options`: an optional object with the following properties:
+ - `optimisticData`: same as `mutate`'s `optimisticData`
+ - `revalidate = true`: same as `mutate`'s `revalidate`
+ - `populateCache = false`: same as `mutate`'s `populateCache`, but the default is `false`
+ - `rollbackOnError = true`: same as `mutate`'s `rollbackOnError`
+ - `throwOnError = true`: same as `mutate`'s `throwOnError`
+ - `onSuccess(data, key, config)`: callback function when a remote mutation has been finished successfully
+ - `onError(err, key, config)`: callback function when a remote mutation has returned an error
+
+#### Return Values
+
+- `data`: data for the given key returned from `fetcher`
+- `error`: error thrown by `fetcher` (or undefined)
+- `trigger(arg, options)`: a function to trigger a remote mutation
+- `reset`: a function to reset the state (`data`, `error`, `isMutating`)
+- `isMutating`: if there's an ongoing remote mutation
+
+### Basic Usage
+
+```jsx
+import useSWRMutation from 'swr/mutation'
+
+async function sendRequest(url, { arg }) {
+ return fetch(url, {
+ method: 'POST',
+ body: JSON.stringify(arg)
+ })
+}
+
+function App() {
+ const { trigger, isMutating } = useSWRMutation('/api/user', sendRequest, /* options */)
+
+ return (
+ {
+ try {
+ const result = await trigger({ username: 'johndoe' }, /* options */)
+ } catch (e) {
+ // error handling
+ }
+ }}
+ >
+ Create User
+
+ )
+}
+```
+
+If you want to use the mutation results in rendering, you can get them from the return values of `useSWRMutation`.
-## Оптимистичные обновления
+```jsx
+const { trigger, data, error } = useSWRMutation('/api/user', sendRequest)
+```
+
+`useSWRMutation` shares a cache store with `useSWR`, so it can detect and avoid race conditions between `useSWR`. It also supports `mutate`'s functionalities like optimistic updates and rollback on errors. You can pass these options `useSWRMutation` and its `trigger` function.
+
+```jsx
+const { trigger } = useSWRMutation('/api/user', updateUser, {
+ optimisticData: current => ({ ...current, name: newName })
+})
+
+// or
+
+trigger(newName, {
+ optimisticData: current => ({ ...current, name: newName })
+})
+```
+
+### Defer loading data until needed
+
+You can also use `useSWRMutation` for loading data. `useSWRMutation` never start requesting until `trigger` is called, so you can defer loading data when you actually need it.
+
+```jsx
+import { useState } from 'react'
+import useSWRMutation from 'swr/mutation'
+
+const fetcher = url => fetch(url).then(res => res.json())
+
+const Page = () => {
+ const [show, setShow] = useState(false)
+ // data is undefined until trigger is called
+ const { data: user, trigger } = useSWRMutation('/api/user', fetcher);
+
+ return (
+
+
{
+ trigger();
+ setShow(true);
+ }}>Show User
+ {show && user ?
{usre.name}
: null}
+
+ );
+}
+```
+
+## Optimistic Updates
-Во многих случаях применение локальных мутаций к данным — хороший способ ускорить внесение изменений — не нужно ждать удаленного источника данных.
+In many cases, applying local mutations to data is a good way to make changes
+feel faster — no need to wait for the remote source of data.
-С помощью `mutate` вы можете обновлять локальные данные программно, пока идёт ревалидация и, в итоге, заменить их свежими данными.
+With the `optimisticData` option, you can update your local data manually, while
+waiting for the remote mutation to finish. Composing `rollbackOnError` you can also
+control when to rollback the data.
```jsx
import useSWR, { useSWRConfig } from 'swr'
@@ -57,48 +270,89 @@ function Profile () {
return (
-
Меня зовут {data.name}.
+ My name is {data.name}.
{
const newName = data.name.toUpperCase()
const user = { ...data, name: newName }
- const options = { optimisticData: user, rollbackOnError: true }
-
- // немедленно обновляет локальные данные
- // отправляем запрос на обновление данных
- // запускает ревалидацию (обновление), чтобы убедиться, что наши локальные данные верны
+ const options = {
+ optimisticData: user,
+ rollbackOnError(error) {
+ // If it's timeout abort error, don't rollback
+ return error.name !== 'AbortError'
+ },
+ }
+
+ // updates the local data immediately
+ // send a request to update the data
+ // triggers a revalidation (refetch) to make sure our local data is correct
mutate('/api/user', updateFn(user), options);
- }}>Перевести моё имя в верхнии регистр!
+ }}>Uppercase my name!
)
}
```
-> **`updateFn`** должена быть промисом или асинхронной функцией для обработки удаленной мутации, она должна возвращать обновленные данные.
+> The **`updateFn`** should be a promise or asynchronous function to handle the remote mutation, it should return updated data.
-## Мутировать на основе текущих данных
+You can also pass a function to `optimisticData` to make it depending on the current data:
-Иногда вы хотите обновить часть ваших данных на основе текущих данных.
+```jsx
+import useSWR, { useSWRConfig } from 'swr'
-С помощью `mutate` вы можете передать асинхронную функцию, которая получит текущее закешированное значение, если оно есть, и вернет обновленный документ.
+function Profile () {
+ const { mutate } = useSWRConfig()
+ const { data } = useSWR('/api/user', fetcher)
+
+ return (
+
+
My name is {data.name}.
+ {
+ const newName = data.name.toUpperCase()
+ mutate('/api/user', updateUserName(newName), {
+ optimisticData: user => ({ ...user, name: newName }),
+ rollbackOnError: true
+ });
+ }}>Uppercase my name!
+
+ )
+}
+```
+
+You can also create the same thing with `useSWRMutation` and `trigger`:
```jsx
-mutate('/api/todos', async todos => {
- // давайте обновим todo с ID равным `1`, и пометим его завершённым,
- // этот API возвращает обновлённые данные
- const updatedTodo = await fetch('/api/todos/1', {
- method: 'PATCH',
- body: JSON.stringify({ completed: true })
- })
+import useSWRMutation from 'swr/mutation'
- // отфильтруем список и вернём его с обновлённым элементом
- const filteredTodos = todos.filter(todo => todo.id !== '1')
- return [...filteredTodos, updatedTodo]
-// Since the API already gives us the updated information,
-// we don't need to revalidate here.
-}, { revalidate: false })
+function Profile () {
+ const { trigger } = useSWRMutation('/api/user', updateUserName)
+
+ return (
+
+
My name is {data.name}.
+ {
+ const newName = data.name.toUpperCase()
+
+ trigger(newName, {
+ optimisticData: user => ({ ...user, name: newName }),
+ rollbackOnError: true
+ })
+ }}>Uppercase my name!
+
+ )
+}
```
-You can also use the `populateCache` option.
+## Rollback on Errors
+
+When you have `optimisticData` set, it’s possible that the optimistic data gets
+displayed to the user, but the remote mutation fails. In this case, you can leverage
+`rollbackOnError` to revert the local cache to the previous state, to make sure
+the user is seeing the correct data.
+
+## Update Cache After Mutation
+
+Sometimes, the remote mutation request directly returns the updated data, so there is no need to do an extra fetch to load it.
+You can enable the `populateCache` option to update the cache for `useSWR` with the response of the mutation:
```jsx
const updateTodo = () => fetch('/api/todos/1', {
@@ -118,44 +372,120 @@ mutate('/api/todos', updateTodo, {
})
```
-## Возвращенные данные из мутации
+Or with the `useSWRMutation` hook:
-Скорее всего, вам понадобятся данные для обновления кеша. Данные разрешаются или возвращаются из промиса или асинхронной функции, переданной в `mutate`.
+```jsx
+useSWRMutation('/api/todos', updateTodo, {
+ populateCache: (updatedTodo, todos) => {
+ // filter the list, and return it with the updated item
+ const filteredTodos = todos.filter(todo => todo.id !== '1')
+ return [...filteredTodos, updatedTodo]
+ },
+ // Since the API already gives us the updated information,
+ // we don't need to revalidate here.
+ revalidate: false
+})
+```
-Функция, переданная в `mutate`, вернёт обновлённый документ, который используется для обновления соответствующего значения кеша. Если при выполнении функции выбрасывается ошибка, она будет выведена, чтобы её можно было обработать должным образом.
+When combined with `optimisticData` and `rollbackOnError`, you’ll get a perfect optimistic UI experience.
-```jsx
-try {
- const user = await mutate('/api/user', updateUser(newUser))
-} catch (error) {
- // Обработайте ошибку здесь при обновлении пользователя
+## Avoid Race Conditions
+
+Both `mutate` and `useSWRMutation` can avoid race conditions between `useSWR`. For example,
+
+```tsx
+function Profile() {
+ const { data } = useSWR('/api/user', getUser, { revalidateInterval: 3000 })
+ const { trigger } = useSWRMutation('/api/user', updateUser)
+
+ return <>
+ {data ? data.username : null}
+ trigger()}>Update User
+ >
}
```
-## Связывание мутации
+The normal `useSWR` hook might refresh its data any time due to focus, polling, or other conditions. This way the displayed username
+can be as fresh as possible. However, since we have a mutation there that can happen at the nearly same time of a refetch of `useSWR`, there
+could be a race condition that `getUser` request starts earlier, but takes longer than `updateUser`.
+
+Luckily, `useSWRMutation` handles this for you automatically. After the mutation, it will tell `useSWR` to ditch the ongoing request and revalidate,
+so the stale data will never be displayed.
+
+## Mutate Based on Current Data
-Объект SWR, возвращаемый `useSWR`, также содержит функцию `mutate()`, которая предварительно привязана к ключу SWR.
+Sometimes, you want to update a part of your data based on the current data.
-Функционально она эквивалентна глобальной функции `mutate`, но не требует параметра `key`.
+With `mutate`, you can pass an async function which will receive the current cached value, if any, and returns an updated document.
```jsx
-import useSWR from 'swr'
+mutate('/api/todos', async todos => {
+ // let's update the todo with ID `1` to be completed,
+ // this API returns the updated data
+ const updatedTodo = await fetch('/api/todos/1', {
+ method: 'PATCH',
+ body: JSON.stringify({ completed: true })
+ })
-function Profile () {
- const { data, mutate } = useSWR('/api/user', fetcher)
+ // filter the list, and return it with the updated item
+ const filteredTodos = todos.filter(todo => todo.id !== '1')
+ return [...filteredTodos, updatedTodo]
+// Since the API already gives us the updated information,
+// we don't need to revalidate here.
+}, { revalidate: false })
+```
- return (
-
-
Меня зовут {data.name}.
- {
- const newName = data.name.toUpperCase()
- // отправьте API запрос для обновления данных
- await requestUpdateUsername(newName)
- // немедленно обновите локальные данные и ревалидируйте их (повторная выборка)
- // ПРИМЕЧАНИЕ: ключ не требуется при использовании мутации useSWR, поскольку он предварительно привязан
- mutate({ ...data, name: newName })
- }}>Перевести моё имя в верхний регистр!
-
- )
-}
+## Mutate Multiple Items
+
+The global `mutate` API accepts a filter function, which accepts `key` as the argument and returns which keys to revalidate. The filter function is applied to all the existing cache keys:
+
+```jsx
+import { mutate } from 'swr'
+// Or from the hook if you customized the cache provider:
+// { mutate } = useSWRConfig()
+
+mutate(
+ key => typeof key === 'string' && key.startsWith('/api/item?id='),
+ undefined,
+ { revalidate: true }
+)
+```
+
+This also works with any key type like an array. The mutation matches all keys, of which the first element is `'item'`.
+
+```jsx
+useSWR(['item', 123], ...)
+useSWR(['item', 124], ...)
+useSWR(['item', 125], ...)
+
+mutate(
+ key => Array.isArray(key) && key[0] === 'item',
+ undefined,
+ { revalidate: false }
+)
+```
+
+The filter function is applied to all existing cache keys, so you should not assume the shape of keys when using multiple shapes of keys.
+
+```jsx
+// ✅ matching array key
+mutate((key) => key[0].startsWith('/api'), data)
+// ✅ matching string key
+mutate((key) => typeof key === 'string' && key.startsWith('/api'), data)
+
+// ❌ ERROR: mutate uncertain keys (array or string)
+mutate((key: any) => /\/api/.test(key.toString()))
+```
+
+You can use the filter function to clear all cache data, which is useful when logging out:
+
+```js
+const clearCache = () => mutate(
+ () => true,
+ undefined,
+ { revalidate: false }
+)
+
+// ...clear cache on logout
+clearCache()
```
diff --git a/pages/docs/mutation.zh-CN.md b/pages/docs/mutation.zh-CN.md
index c3b0e109..14a9d8dc 100644
--- a/pages/docs/mutation.zh-CN.md
+++ b/pages/docs/mutation.zh-CN.md
@@ -1,19 +1,69 @@
-# 数据更改
+# Mutation & Revalidation
-## 选项
+SWR provides the [`mutate`](/docs/mutation#mutate) and [`useSWRMutation`](/docs/mutation#useswrmutation) APIs for mutating remote data and related cache.
-- `optimisticData`:立即更新客户端缓存的数据,通常用于 optimistic UI。
-- `revalidate`:一旦完成异步更新,缓存是否重新请求。
-- `populateCache`:远程更新的结果是否写入缓存,或者是一个以新结果和当前结果作为参数并返回更新结果的函数。
-- `rollbackOnError`:如果远程更新出错,是否进行缓存回滚。
+## `mutate`
-## 重新验证
+There're 2 ways to use the `mutate` API to mutate the data, the global mutate API which can mutate any key and the bound mutate API which only can mutate the data of corresponding SWR hook.
-你可以使用 `useSWRConfig()` 所返回的 `mutate` 函数,来广播重新验证的消息给其他的 SWR hook* 。使用同一个 key 调用 `mutate(key)` 即可。
+#### Global Mutate
-以下示例显示了当用户点击 “注销” 按钮时如何自动重新请求登录信息(例如在 ` ` 内):
+The recommended way to get the global mutator is to use the [`useSWRConfig`](/docs/global-configuration#access-to-global-configurations) hook:
+
+```js
+import { useSWRConfig } from "swr"
+
+function App() {
+ const { mutate } = useSWRConfig()
+ mutate(key, data, options)
+}
+```
+
+You can also import it globally:
+
+```js
+import { mutate } from "swr"
+
+function App() {
+ mutate(key, data, options)
+}
+```
+
+#### Bound Mutate
+
+Bound mutate is the short path to mutate the current key with data. Which `key` is bounded to the `key` passing to `useSWR`, and receive the `data` as the first argument.
+
+It is functionally equivalent to the global `mutate` function in the previous section but does not require the `key` parameter:
```jsx
+import useSWR from 'swr'
+
+function Profile () {
+ const { data, mutate } = useSWR('/api/user', fetcher)
+
+ return (
+
+
My name is {data.name}.
+ {
+ const newName = data.name.toUpperCase()
+ // send a request to the API to update the data
+ await requestUpdateUsername(newName)
+ // update the local data immediately and revalidate (refetch)
+ // NOTE: key is not required when using useSWR's mutate as it's pre-bound
+ mutate({ ...data, name: newName })
+ }}>Uppercase my name!
+
+ )
+}
+```
+
+#### Revalidation
+
+When you call `mutate(key)` (or just `mutate()` with the bound mutate API) without any data, it will trigger a revalidation (mark the data as expired and trigger a refetch)
+for the resource. This example shows how to automatically refetch the login info (e.g. inside ` `)
+when the user clicks the “Logout” button:
+
+```jsx {14}
import useSWR, { useSWRConfig } from 'swr'
function App () {
@@ -23,10 +73,10 @@ function App () {
{
- // 将 cookie 设置为过期
+ // set the cookie as expired
document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'
- // 告诉所有具有该 key 的 SWR 重新验证
+ // tell all SWRs with this key to revalidate
mutate('/api/user')
}}>
Logout
@@ -36,13 +86,180 @@ function App () {
}
```
-*: _通常情况下 mutate 会广播给同一个 [cache provider](/docs/cache) 下面的 SWR hooks。如果没有设置 cache provider,即会广播给所有的 SWR hooks。_
+import Callout from 'nextra-theme-docs/callout'
+
+
+It broadcasts to SWR hooks under the same [cache provider](/docs/advanced/cache) scope. If no cache provider exists, it will broadcast to all SWR hooks.
+
+
+
+### API
+
+#### Parameters
+
+- `key`: same as `useSWR`'s `key`, but a function behaves as [a filter function](/docs/mutation#mutate-multiple-items)
+- `data`: data to update the client cache, or an async function for the remote mutation
+- `options`: accepts the following options
+ - `optimisticData`: data to immediately update the client cache, or a function that receives current data and returns the new client cache data, usually used in optimistic UI.
+ - `revalidate = true`: should the cache revalidate once the asynchronous update resolves.
+ - `populateCache = true`: should the result of the remote mutation be written to the cache, or a function that receives new result and current result as arguments and returns the mutation result.
+ - `rollbackOnError = true`: should the cache rollback if the remote mutation errors, or a function that receives the error thrown from fetcher as arguments and returns a boolean whether should rollback or not.
+ - `throwOnError = true`: should the mutate call throw the error when fails.
+
+#### Return Values
+
+`mutate` returns the results the `data` parameter has been resolved. The function passed to `mutate` will return an updated data which is used to update the corresponding cache value. If there is an error thrown while executing the function, the error will be thrown so it can be handled appropriately.
+
+```jsx
+try {
+ const user = await mutate('/api/user', updateUser(newUser))
+} catch (error) {
+ // Handle an error while updating the user here
+}
+```
+
+## `useSWRMutation`
+
+SWR also provides `useSWRMutation` as a hook for remote mutations. The remote mutations are only triggered manually, instead of automatically like `useSWR`.
+
+Also, this hook doesn’t share states with other `useSWRMutation` hooks.
+
+```jsx
+import useSWRMutation from 'swr/mutation'
+
+// Fetcher implementation.
+// The extra argument will be passed via the `arg` property of the 2nd parameter.
+// In the example below, `arg` will be `'my_token'`
+async function updateUser(url, { arg }) {
+ await fetch(url, {
+ method: 'POST',
+ headers: {
+ Authorization: `Bearer ${arg}`
+ }
+ })
+}
+
+function Profile() {
+ // A useSWR + mutate like API, but it will not start the request automatically.
+ const { trigger } = useSWRMutation('/api/user', updateUser, options?)
+
+ return {
+ // Trigger `updateUser` with a specific argument.
+ trigger('my_token')
+ }}>Update User
+}
+```
+
+### API
+
+#### Parameters
+
+- `key`: same as [`mutate`](/docs/mutation#mutate)'s `key`
+- `fetcher(key, { arg })`: an async function for remote mutation
+- `options`: an optional object with the following properties:
+ - `optimisticData`: same as `mutate`'s `optimisticData`
+ - `revalidate = true`: same as `mutate`'s `revalidate`
+ - `populateCache = false`: same as `mutate`'s `populateCache`, but the default is `false`
+ - `rollbackOnError = true`: same as `mutate`'s `rollbackOnError`
+ - `throwOnError = true`: same as `mutate`'s `throwOnError`
+ - `onSuccess(data, key, config)`: callback function when a remote mutation has been finished successfully
+ - `onError(err, key, config)`: callback function when a remote mutation has returned an error
+
+#### Return Values
+
+- `data`: data for the given key returned from `fetcher`
+- `error`: error thrown by `fetcher` (or undefined)
+- `trigger(arg, options)`: a function to trigger a remote mutation
+- `reset`: a function to reset the state (`data`, `error`, `isMutating`)
+- `isMutating`: if there's an ongoing remote mutation
+
+### Basic Usage
+
+```jsx
+import useSWRMutation from 'swr/mutation'
+
+async function sendRequest(url, { arg }) {
+ return fetch(url, {
+ method: 'POST',
+ body: JSON.stringify(arg)
+ })
+}
+
+function App() {
+ const { trigger, isMutating } = useSWRMutation('/api/user', sendRequest, /* options */)
+
+ return (
+ {
+ try {
+ const result = await trigger({ username: 'johndoe' }, /* options */)
+ } catch (e) {
+ // error handling
+ }
+ }}
+ >
+ Create User
+
+ )
+}
+```
+
+If you want to use the mutation results in rendering, you can get them from the return values of `useSWRMutation`.
+
+```jsx
+const { trigger, data, error } = useSWRMutation('/api/user', sendRequest)
+```
+
+`useSWRMutation` shares a cache store with `useSWR`, so it can detect and avoid race conditions between `useSWR`. It also supports `mutate`'s functionalities like optimistic updates and rollback on errors. You can pass these options `useSWRMutation` and its `trigger` function.
+
+```jsx
+const { trigger } = useSWRMutation('/api/user', updateUser, {
+ optimisticData: current => ({ ...current, name: newName })
+})
+
+// or
+
+trigger(newName, {
+ optimisticData: current => ({ ...current, name: newName })
+})
+```
+
+### Defer loading data until needed
+
+You can also use `useSWRMutation` for loading data. `useSWRMutation` never start requesting until `trigger` is called, so you can defer loading data when you actually need it.
+
+```jsx
+import { useState } from 'react'
+import useSWRMutation from 'swr/mutation'
+
+const fetcher = url => fetch(url).then(res => res.json())
+
+const Page = () => {
+ const [show, setShow] = useState(false)
+ // data is undefined until trigger is called
+ const { data: user, trigger } = useSWRMutation('/api/user', fetcher);
+
+ return (
+
+
{
+ trigger();
+ setShow(true);
+ }}>Show User
+ {show && user ?
{usre.name}
: null}
+
+ );
+}
+```
-## 乐观更新
+## Optimistic Updates
-在很多情况中,对数据应用本地 mutation 是一种让更改感觉更快的好办法 - 无需等待远程数据源。
+In many cases, applying local mutations to data is a good way to make changes
+feel faster — no need to wait for the remote source of data.
-使用 `mutate`,你可以以编程方式更新本地数据,同时重新验证并最终将其替换为最新数据。
+With the `optimisticData` option, you can update your local data manually, while
+waiting for the remote mutation to finish. Composing `rollbackOnError` you can also
+control when to rollback the data.
```jsx
import useSWR, { useSWRConfig } from 'swr'
@@ -57,43 +274,85 @@ function Profile () {
{
const newName = data.name.toUpperCase()
const user = { ...data, name: newName }
- const options = { optimisticData: user, rollbackOnError: true }
-
- // 立即更新本地数据
- // 发送请求更新数据
- // 触发重新验证(重新请求)以确保本地数据是正确的
+ const options = {
+ optimisticData: user,
+ rollbackOnError(error) {
+ // If it's timeout abort error, don't rollback
+ return error.name !== 'AbortError'
+ },
+ }
+
+ // updates the local data immediately
+ // send a request to update the data
+ // triggers a revalidation (refetch) to make sure our local data is correct
mutate('/api/user', updateFn(user), options);
}}>Uppercase my name!
)
}
```
-> **`updateFn`** 应该是一个 promise 或异步函数来处理远程更新,它应该返回更新后的数据。
-## 根据当前数据更改
+> The **`updateFn`** should be a promise or asynchronous function to handle the remote mutation, it should return updated data.
-有时,你想基于当前数据更新部分你的数据。
+You can also pass a function to `optimisticData` to make it depending on the current data:
-使用 `mutate`,你可以传递一个异步函数,该函数将接收当前缓存的值(如果有的话),并返回一个 updated document。
+```jsx
+import useSWR, { useSWRConfig } from 'swr'
+
+function Profile () {
+ const { mutate } = useSWRConfig()
+ const { data } = useSWR('/api/user', fetcher)
+
+ return (
+
+
My name is {data.name}.
+ {
+ const newName = data.name.toUpperCase()
+ mutate('/api/user', updateUserName(newName), {
+ optimisticData: user => ({ ...user, name: newName }),
+ rollbackOnError: true
+ });
+ }}>Uppercase my name!
+
+ )
+}
+```
+
+You can also create the same thing with `useSWRMutation` and `trigger`:
```jsx
-mutate('/api/todos', async todos => {
- // 把 ID 为 1 的更新为 completed,
- // 该 API 返回更新后的数据
- const updatedTodo = await fetch('/api/todos/1', {
- method: 'PATCH',
- body: JSON.stringify({ completed: true })
- })
+import useSWRMutation from 'swr/mutation'
- // 筛选列表,返回更新后的 item
- const filteredTodos = todos.filter(todo => todo.id !== '1')
- return [...filteredTodos, updatedTodo]
- // API 已经给我们提供了更新的信息,
- // 所以我们不需要在这里重新请求。
-}, { revalidate: false })
+function Profile () {
+ const { trigger } = useSWRMutation('/api/user', updateUserName)
+
+ return (
+
+
My name is {data.name}.
+ {
+ const newName = data.name.toUpperCase()
+
+ trigger(newName, {
+ optimisticData: user => ({ ...user, name: newName }),
+ rollbackOnError: true
+ })
+ }}>Uppercase my name!
+
+ )
+}
```
-还可以使用 `populateCache`。
+## Rollback on Errors
+
+When you have `optimisticData` set, it’s possible that the optimistic data gets
+displayed to the user, but the remote mutation fails. In this case, you can enable
+`rollbackOnError` to revert the local cache to the previous state, to make sure
+the user is seeing the correct data.
+
+## Update Cache After Mutation
+
+Sometimes, the remote mutation request directly returns the updated data, so there is no need to do an extra fetch to load it.
+You can enable the `populateCache` option to update the cache for `useSWR` with the response of the mutation:
```jsx
const updateTodo = () => fetch('/api/todos/1', {
@@ -103,54 +362,130 @@ const updateTodo = () => fetch('/api/todos/1', {
mutate('/api/todos', updateTodo, {
populateCache: (updatedTodo, todos) => {
- // 筛选列表,返回更新后的 item
+ // filter the list, and return it with the updated item
+ const filteredTodos = todos.filter(todo => todo.id !== '1')
+ return [...filteredTodos, updatedTodo]
+ },
+ // Since the API already gives us the updated information,
+ // we don't need to revalidate here.
+ revalidate: false
+})
+```
+
+Or with the `useSWRMutation` hook:
+
+```jsx
+useSWRMutation('/api/todos', updateTodo, {
+ populateCache: (updatedTodo, todos) => {
+ // filter the list, and return it with the updated item
const filteredTodos = todos.filter(todo => todo.id !== '1')
return [...filteredTodos, updatedTodo]
},
- // API 已经给我们提供了更新的信息,
- // 所以我们不需要在这里重新请求。
+ // Since the API already gives us the updated information,
+ // we don't need to revalidate here.
revalidate: false
})
```
-## Mutate 的返回值
+When combined with `optimisticData` and `rollbackOnError`, you’ll get a perfect optimistic UI experience.
-最有可能的是,你需要一些数据来更新缓存。这些数据是从你传递给 `mutate` 的 promise 或 异步函数解析或返回的。
+## Avoid Race Conditions
-该函数将返回一个 updated document,让 `mutate` 更新相应的缓存值。每次调用它时,都可能以某种方式抛出错误。
+Both `mutate` and `useSWRMutation` can avoid race conditions between `useSWR`. For example,
-```jsx
-try {
- const user = await mutate('/api/user', updateUser(newUser))
-} catch (error) {
- // 在这里处理更新 user 时的错误
+```tsx
+function Profile() {
+ const { data } = useSWR('/api/user', getUser, { revalidateInterval: 3000 })
+ const { trigger } = useSWRMutation('/api/user', updateUser)
+
+ return <>
+ {data ? data.username : null}
+ trigger()}>Update User
+ >
}
```
-## 绑定的 Mutate 函数
+The normal `useSWR` hook might refresh its data any time due to focus, polling, or other conditions. This way the displayed username
+can be as fresh as possible. However, since we have a mutation there that can happen at the nearly same time of a refetch of `useSWR`, there
+could be a race condition that `getUser` request starts earlier, but takes longer than `updateUser`.
+
+Luckily, `useSWRMutation` handles this for you automatically. After the mutation, it will tell `useSWR` to ditch the ongoing request and revalidate,
+so the stale data will never be displayed.
-`useSWR` 返回的 SWR 对象还包含一个 `mutate()` 函数,该函数预先绑定到 SWR 的 key。
+## Mutate Based on Current Data
-它在功能上等同于全局 `mutate` 函数,但不需要 `key` 参数。
+Sometimes, you want to update a part of your data based on the current data.
+
+With `mutate`, you can pass an async function which will receive the current cached value, if any, and returns an updated document.
```jsx
-import useSWR from 'swr'
+mutate('/api/todos', async todos => {
+ // let's update the todo with ID `1` to be completed,
+ // this API returns the updated data
+ const updatedTodo = await fetch('/api/todos/1', {
+ method: 'PATCH',
+ body: JSON.stringify({ completed: true })
+ })
-function Profile () {
- const { data, mutate } = useSWR('/api/user', fetcher)
+ // filter the list, and return it with the updated item
+ const filteredTodos = todos.filter(todo => todo.id !== '1')
+ return [...filteredTodos, updatedTodo]
+// Since the API already gives us the updated information,
+// we don't need to revalidate here.
+}, { revalidate: false })
+```
- return (
-
-
My name is {data.name}.
- {
- const newName = data.name.toUpperCase()
- // 向 API 发送请求以更新数据
- await requestUpdateUsername(newName)
- // 立即更新本地数据并重新验证 (重新请求)
- // 注意:在使用 useSWR 的 mutate 时,key 是不需要的,因为它是预先绑定的
- mutate({ ...data, name: newName })
- }}>Uppercase my name!
-
- )
-}
+## Mutate Multiple Items
+
+The global `mutate` API accepts a filter function, which accepts `key` as the argument and returns which keys to revalidate. The filter function is applied to all the existing cache keys:
+
+```jsx
+import { mutate } from 'swr'
+// Or from the hook if you customized the cache provider:
+// { mutate } = useSWRConfig()
+
+mutate(
+ key => typeof key === 'string' && key.startsWith('/api/item?id='),
+ undefined,
+ { revalidate: true }
+)
+```
+
+This also works with any key type like an array. The mutation matches all keys, of which the first element is `'item'`.
+
+```jsx
+useSWR(['item', 123], ...)
+useSWR(['item', 124], ...)
+useSWR(['item', 125], ...)
+
+mutate(
+ key => Array.isArray(key) && key[0] === 'item',
+ undefined,
+ { revalidate: false }
+)
+```
+
+The filter function is applied to all existing cache keys, so you should not assume the shape of keys when using multiple shapes of keys.
+
+```jsx
+// ✅ matching array key
+mutate((key) => key[0].startsWith('/api'), data)
+// ✅ matching string key
+mutate((key) => typeof key === 'string' && key.startsWith('/api'), data)
+
+// ❌ ERROR: mutate uncertain keys (array or string)
+mutate((key: any) => /\/api/.test(key.toString()))
+```
+
+You can use the filter function to clear all cache data, which is useful when logging out:
+
+```js
+const clearCache = () => mutate(
+ () => true,
+ undefined,
+ { revalidate: false }
+)
+
+// ...clear cache on logout
+clearCache()
```
diff --git a/pages/docs/pagination.en-US.mdx b/pages/docs/pagination.en-US.mdx
index 43c243dd..9e3f9ab2 100644
--- a/pages/docs/pagination.en-US.mdx
+++ b/pages/docs/pagination.en-US.mdx
@@ -172,7 +172,7 @@ That's how this new `useSWRInfinite` Hook can help.
import useSWRInfinite from 'swr/infinite'
// ...
-const { data, error, isValidating, mutate, size, setSize } = useSWRInfinite(
+const { data, error, isLoading, isValidating, mutate, size, setSize } = useSWRInfinite(
getKey, fetcher?, options?
)
```
@@ -207,6 +207,7 @@ In infinite loading, one _page_ is one request, and our goal is to fetch multipl
- `data`: an array of fetch response values of each page
- `error`: same as `useSWR`'s `error`
+- `isLoading`: same as `useSWR`'s `isLoading`
- `isValidating`: same as `useSWR`'s `isValidating`
- `mutate`: same as `useSWR`'s bound mutate function but manipulates the data array
- `size`: the number of pages that _will_ be fetched and returned
diff --git a/pages/docs/pagination.es-ES.mdx b/pages/docs/pagination.es-ES.mdx
index 0e94f6c7..09126f1c 100644
--- a/pages/docs/pagination.es-ES.mdx
+++ b/pages/docs/pagination.es-ES.mdx
@@ -174,7 +174,7 @@ Así es como este nuevo hook `useSWRInfinite` puede ayudar.
import useSWRInfinite from 'swr/infinite'
// ...
-const { data, error, isValidating, mutate, size, setSize } = useSWRInfinite(
+const { data, error, isLoading, isValidating, mutate, size, setSize } = useSWRInfinite(
getKey, fetcher?, options?
)
```
@@ -209,6 +209,7 @@ En la carga infinita, una _page_ es una petición, y nuestro objetivo es obtener
- `data`: una array de valores de respuesta fetch de cada página
- `error`: El mismo valor devuelto de `error` que `useSWR`
+- `isLoading`: El mismo valor devuelto de `isLoading` que `useSWR`
- `isValidating`: El mismo valor devuelto de `isValidating` que `useSWR`
- `mutate`: same as `useSWR`'s bound mutate function but manipulates the data array
- `size`: el número de páginas que _se obtendrán_ y devolverán
diff --git a/pages/docs/pagination.ja.mdx b/pages/docs/pagination.ja.mdx
index fc2f51bd..f1c3c7eb 100644
--- a/pages/docs/pagination.ja.mdx
+++ b/pages/docs/pagination.ja.mdx
@@ -172,7 +172,7 @@ function App () {
import useSWRInfinite from 'swr/infinite'
// ...
-const { data, error, isValidating, mutate, size, setSize } = useSWRInfinite(
+const { data, error, isLoading, isValidating, mutate, size, setSize } = useSWRInfinite(
getKey, fetcher?, options?
)
```
@@ -183,7 +183,7 @@ const { data, error, isValidating, mutate, size, setSize } = useSWRInfinite(
無限ローディングでは、1 *ページ*が一つのリクエストであり、目標は複数ページをフェッチしてレンダリングすることです。
- もし SWR 0.x バージョンを使っている場合は、 `swr` から `useSWRInfinite` をインポートする必要があります:
+ もし SWR 0.x バージョンを使っている場合は、 `swr` から `useSWRInfinite` をインポートする必要があります:
`import { useSWRInfinite } from 'swr'`
@@ -207,6 +207,7 @@ const { data, error, isValidating, mutate, size, setSize } = useSWRInfinite(
- `data`: 各ページのフェッチしたレスポンス値の配列
- `error`: `useSWR` の `error` と同じ
+- `isLoading`: `useSWR` の `isLoading` と同じ
- `isValidating`: `useSWR` の `isValidating` と同じ
- `mutate`: `useSWR` のバインドされたミューテート関数と同じですが、データ配列を操作します
- `size`: フェッチして返される*だろう*ページ数
diff --git a/pages/docs/pagination.ko.mdx b/pages/docs/pagination.ko.mdx
index 6f769dd1..6b8b7988 100644
--- a/pages/docs/pagination.ko.mdx
+++ b/pages/docs/pagination.ko.mdx
@@ -172,7 +172,7 @@ function App () {
import useSWRInfinite from 'swr/infinite'
// ...
-const { data, error, isValidating, mutate, size, setSize } = useSWRInfinite(
+const { data, error, isLoading, isValidating, mutate, size, setSize } = useSWRInfinite(
getKey, fetcher?, options?
)
```
@@ -207,6 +207,7 @@ const { data, error, isValidating, mutate, size, setSize } = useSWRInfinite(
- `data`: 각 페이지의 가져오기 응답 값의 배열
- `error`: `useSWR`의 `error`와 동일
+- `isLoading`: `useSWR`의 `isLoading`과 동일
- `isValidating`: `useSWR`의 `isValidating`과 동일
- `mutate`: `useSWR`의 바인딩 된 뮤테이트 함수와 동일하지만 데이터 배열을 다룸
- `size`: 가져*올* 페이지 및 반환*될* 페이지의 수
diff --git a/pages/docs/pagination.pt-BR.mdx b/pages/docs/pagination.pt-BR.mdx
index 664010f2..32f2f320 100644
--- a/pages/docs/pagination.pt-BR.mdx
+++ b/pages/docs/pagination.pt-BR.mdx
@@ -1,322 +1,323 @@
-import Callout from 'nextra-theme-docs/callout'
-
-# Paginação
-
-
- Por favor atualize para a versão mais recente (≥ 0.3.0) para usar esta API. A API anterior useSWRPages
está descontinuada.
-
-
-SWR provê uma API `useSWRInfinite` dedicada para lidar com padrões de interface comuns como **paginação** e **carregamento infinito**.
-
-## Quando Usar `useSWR`
-
-### Paginação
-
-Antes de tudo, nós podemos **NÃO** precisar de `useSWRInfinite` mas podemos usar apenas `useSWR` se estamos construindo algo como isso:
-
-import { Pagination } from 'components/diagrams/pagination'
-
-
-
-...que é uma interface típica de paginação. Veja como pode ser facilmente implementado com `useSWR`:
-
-```jsx {5}
-function App () {
- const [pageIndex, setPageIndex] = useState(0);
-
- // A URL da API inclui o índice da página, que é um estado do React.
- const { data } = useSWR(`/api/data?page=${pageIndex}`, fetcher);
-
- // ... lidando com estados de loading e erro
-
- return
- {data.map(item =>
{item.name}
)}
-
setPageIndex(pageIndex - 1)}>Previous
-
setPageIndex(pageIndex + 1)}>Next
-
-}
-```
-
-Além disso, podemos criar uma abstração para este "componente de página":
-
-```jsx {13}
-function Page ({ index }) {
- const { data } = useSWR(`/api/data?page=${index}`, fetcher);
-
- // ... lidando com estados de loading e erro
-
- return data.map(item => {item.name}
)
-}
-
-function App () {
- const [pageIndex, setPageIndex] = useState(0);
-
- return
-
- setPageIndex(pageIndex - 1)}>Previous
- setPageIndex(pageIndex + 1)}>Next
-
-}
-```
-
-Por causa do cache do SWR, temos o benefício de pré-carregar a próxima página. Renderizamos a próxima página dentro
-um div oculto, SWR acionará a busca de dados da próxima página. Quando o usuário navega para a próxima página, os dados já estão lá:
-
-```jsx {6}
-function App () {
- const [pageIndex, setPageIndex] = useState(0);
-
- return
-
-
- setPageIndex(pageIndex - 1)}>Previous
- setPageIndex(pageIndex + 1)}>Next
-
-}
-```
-
-Com apenas 1 linha de código, obtemos uma UX muito melhor. O hook `useSWR` é tão poderoso que a maioria dos cenários são cobertos por ele.
-
-### Carregamento Infinito
-
-Às vezes, queremos criar uma UI de **carregamento infinito**, com um botão "Carregar mais" que anexa dados
-para a lista (ou feito automaticamente quando você rola):
-
-import { Infinite } from 'components/diagrams/infinite'
-
-
-
-
-
-Para implementar isso, precisamos fazer um **número dinâmico de solicitações** nesta página. Os React Hooks tem [algumas regras](https://pt-br.reactjs.org/docs/hooks-rules.html),
-então nós **NÃO PODEMOS** fazer algo assim:
-
-```jsx {5,6,7,8,9}
-function App () {
- const [cnt, setCnt] = useState(1)
-
- const list = []
- for (let i = 0; i < cnt; i++) {
- // 🚨 Isso é errado! Comumente, você não pode usar hooks dentro de um loop.
- const { data } = useSWR(`/api/data?page=${i}`)
- list.push(data)
- }
-
- return
- {list.map((data, i) =>
-
{
- data.map(item =>
{item.name}
)
- }
)}
-
setCnt(cnt + 1)}>Load More
-
-}
-```
-
-Ao invés disso, podemos usar a abstração ` ` que criamos para conseguir isso:
-
-```jsx {5,6,7}
-function App () {
- const [cnt, setCnt] = useState(1)
-
- const pages = []
- for (let i = 0; i < cnt; i++) {
- pages.push( )
- }
-
- return
- {pages}
- setCnt(cnt + 1)}>Load More
-
-}
-```
-
-### Casos Avançados
-
-Entretanto, em alguns casos avançados, a solução acima não funciona.
-
-Por exemplo, nós ainda estamos implementando o mesmo "Carregar mais", mas também precisamos mostrar um número
-sobre quantos itens existem no total. Nós não podemos usar o ` ` novamente porque
-a top level UI (` `) precisa dos dados dentro de cada página:
-
-```jsx {10}
-function App () {
- const [cnt, setCnt] = useState(1)
-
- const pages = []
- for (let i = 0; i < cnt; i++) {
- pages.push( )
- }
-
- return
-
??? items
- {pages}
-
setCnt(cnt + 1)}>Load More
-
-}
-```
-
-Também, se a API de paginação for **baseada em cursor**, essa solução não funciona também. Porque cada página
-precisa dos dados da página interior, elas não são isoladas.
-
-É assim que esse novo hook `useSWRInfinite` pode ajudar.
-
-## useSWRInfinite
-
-`useSWRInfinite` nos dá a habilidade de disparar uma quantidade de solicitações com um Hook. Isso é como se fosse:
-
-```jsx
-import useSWRInfinite from 'swr/infinite'
-
-// ...
-const { data, error, isValidating, mutate, size, setSize } = useSWRInfinite(
- getKey, fetcher?, options?
-)
-```
-
-Similar ao `useSWR`, esse novo Hook aceita uma função que retorna a chave de pedido, uma função de busca, e opções.
-Ele retorna todos os valores que `useSWR` retorna, incluindo 2 valores extras: o tamanho da página e um setter do tamanho da página, como um state do React.
-
-In infinite loading, one _page_ is one request, and our goal is to fetch multiple pages and render them.
-
-
- Se você está usando versões SWR 0.x, `useSWRInfinite` precisa ser importado de `swr`:
- `import { useSWRInfinite } from 'swr'`
-
-
-### API
-
-#### Parâmetros
-
-- `getKey`: uma função que aceita o índice e os dados da página anterior, retorna a chave de uma página
-- `fetcher`: mesma função que a [função fetcher](/docs/data-fetching) do `useSWR`.
-- `options`: aceita todas as opções que `useSWR` suporta, com 4 opções adicionais:
- - `initialSize=1`: número de páginas devem ser carregadas inicialmente
- - `revalidateAll=false`: sempre tentar revalidar todas as páginas
- - `revalidateFirstPage=true`: sempre tentar revalidar a primeira página
- - `persistSize=false`: não reseta o tamanho da página para 1 (ou `initialSize` se setado) quando a chave da primeira página muda
-
-
- Note que a opção `initialSize` não é permitida a mudar no ciclo de vida.
-
-
-#### Valores de Retorno
-
-- `data`: um array de respostas de fetch para cada página
-- `error`: mesma que o objeto `error` do `useSWR`.
-- `isValidating`: mesmo que o `isValidating` do `useSWR`.
-- `mutate`: igual à função de mutação vinculada do `useSWR`, mas manipula o array de dados
-- `size`: o número de páginas que _vão_ ser carregadas e retornadas
-- `setSize`: define o número de páginas que precisam ser carregadas
-
-### Exemplo 1: API de Paginação Baseada em Índice
-
-Para APIs baseadas em índice:
-
-```plaintext
-GET /users?page=0&limit=10
-[
- { name: 'Alice', ... },
- { name: 'Bob', ... },
- { name: 'Cathy', ... },
- ...
-]
-```
-
-```jsx {4,5,6,7,10}
-// A função para obter a chave SWR de cada página,
-// o valor retornado será aceito pela função `fetcher`.
-// Se o valor `null` é retornado, a solicitação da página não iniciará.
-const getKey = (pageIndex, previousPageData) => {
- if (previousPageData && !previousPageData.length) return null // atingiu o fim
- return `/users?page=${pageIndex}&limit=10` // chave SWR
-}
-
-function App () {
- const { data, size, setSize } = useSWRInfinite(getKey, fetcher)
- if (!data) return 'loading'
-
- // Agora podemos calcular o número total de usuários
- let totalUsers = 0
- for (let i = 0; i < data.length; i++) {
- totalUsers += data[i].length
- }
-
- return
-
{totalUsers} users listed
- {data.map((users, index) => {
- // `data` é um array de respostas da API de cada página.
- return users.map(user =>
{user.name}
)
- })}
-
setSize(size + 1)}>Load More
-
-}
-```
-
-A função `getKey` é a diferença maior entre `useSWRInfinite` e `useSWR`.
-Ela aceita o índice da página atual, bem como os dados da página anterior.
-Então ambas (paginação baseada em índice e páginação baseada em cursor) podem ser suportadas.
-
-Também, `data` não é mais apenas uma resposta da API. É um array de respostas da API:
-
-```js
-// `data` se parecerá com isso
-[
- [
- { name: 'Alice', ... },
- { name: 'Bob', ... },
- { name: 'Cathy', ... },
- ...
- ],
- [
- { name: 'John', ... },
- { name: 'Paul', ... },
- { name: 'George', ... },
- ...
- ],
- ...
-]
-```
-
-### Exemplo 2: API de Paginação Baseada em Cursor ou Offset
-
-Vamos dizer que a API agora requer um cursor e retorna o próximo cursor junto com os dados:
-
-```plaintext
-GET /users?cursor=123&limit=10
-{
- data: [
- { name: 'Alice' },
- { name: 'Bob' },
- { name: 'Cathy' },
- ...
- ],
- nextCursor: 456
-}
-```
-
-Nós podemos mudar nossa função `getKey` para:
-
-```jsx
-const getKey = (pageIndex, previousPageData) => {
- // alcançou o fim
- if (previousPageData && !previousPageData.data) return null
-
- // primeira página, nós não temos `previousPageData`
- if (pageIndex === 0) return `/users?limit=10`
-
- // adiciona o cursor para o endpoint da API
- return `/users?cursor=${previousPageData.nextCursor}&limit=10`
-}
-```
-
-### Funcionalidades Avançadas
-
-[Aqui tem um exemplo](/examples/infinite-loading) mostrando como você pode implementar as seguintes funcionalidades com `useSWRInfinite`:
-
-- estados de carregamento
-- mostrar uma interface especial se estiver vazio
-- desativar o botão "Carregar mais" se alcançou o fim
-- fonte de dados alterável
-- atualizar toda a lista
+import Callout from 'nextra-theme-docs/callout'
+
+# Paginação
+
+
+ Por favor atualize para a versão mais recente (≥ 0.3.0) para usar esta API. A API anterior useSWRPages
está descontinuada.
+
+
+SWR provê uma API `useSWRInfinite` dedicada para lidar com padrões de interface comuns como **paginação** e **carregamento infinito**.
+
+## Quando Usar `useSWR`
+
+### Paginação
+
+Antes de tudo, nós podemos **NÃO** precisar de `useSWRInfinite` mas podemos usar apenas `useSWR` se estamos construindo algo como isso:
+
+import { Pagination } from 'components/diagrams/pagination'
+
+
+
+...que é uma interface típica de paginação. Veja como pode ser facilmente implementado com `useSWR`:
+
+```jsx {5}
+function App () {
+ const [pageIndex, setPageIndex] = useState(0);
+
+ // A URL da API inclui o índice da página, que é um estado do React.
+ const { data } = useSWR(`/api/data?page=${pageIndex}`, fetcher);
+
+ // ... lidando com estados de loading e erro
+
+ return
+ {data.map(item =>
{item.name}
)}
+
setPageIndex(pageIndex - 1)}>Previous
+
setPageIndex(pageIndex + 1)}>Next
+
+}
+```
+
+Além disso, podemos criar uma abstração para este "componente de página":
+
+```jsx {13}
+function Page ({ index }) {
+ const { data } = useSWR(`/api/data?page=${index}`, fetcher);
+
+ // ... lidando com estados de loading e erro
+
+ return data.map(item => {item.name}
)
+}
+
+function App () {
+ const [pageIndex, setPageIndex] = useState(0);
+
+ return
+
+ setPageIndex(pageIndex - 1)}>Previous
+ setPageIndex(pageIndex + 1)}>Next
+
+}
+```
+
+Por causa do cache do SWR, temos o benefício de pré-carregar a próxima página. Renderizamos a próxima página dentro
+um div oculto, SWR acionará a busca de dados da próxima página. Quando o usuário navega para a próxima página, os dados já estão lá:
+
+```jsx {6}
+function App () {
+ const [pageIndex, setPageIndex] = useState(0);
+
+ return
+
+
+ setPageIndex(pageIndex - 1)}>Previous
+ setPageIndex(pageIndex + 1)}>Next
+
+}
+```
+
+Com apenas 1 linha de código, obtemos uma UX muito melhor. O hook `useSWR` é tão poderoso que a maioria dos cenários são cobertos por ele.
+
+### Carregamento Infinito
+
+Às vezes, queremos criar uma UI de **carregamento infinito**, com um botão "Carregar mais" que anexa dados
+para a lista (ou feito automaticamente quando você rola):
+
+import { Infinite } from 'components/diagrams/infinite'
+
+
+
+
+
+Para implementar isso, precisamos fazer um **número dinâmico de solicitações** nesta página. Os React Hooks tem [algumas regras](https://pt-br.reactjs.org/docs/hooks-rules.html),
+então nós **NÃO PODEMOS** fazer algo assim:
+
+```jsx {5,6,7,8,9}
+function App () {
+ const [cnt, setCnt] = useState(1)
+
+ const list = []
+ for (let i = 0; i < cnt; i++) {
+ // 🚨 Isso é errado! Comumente, você não pode usar hooks dentro de um loop.
+ const { data } = useSWR(`/api/data?page=${i}`)
+ list.push(data)
+ }
+
+ return
+ {list.map((data, i) =>
+
{
+ data.map(item =>
{item.name}
)
+ }
)}
+
setCnt(cnt + 1)}>Load More
+
+}
+```
+
+Ao invés disso, podemos usar a abstração ` ` que criamos para conseguir isso:
+
+```jsx {5,6,7}
+function App () {
+ const [cnt, setCnt] = useState(1)
+
+ const pages = []
+ for (let i = 0; i < cnt; i++) {
+ pages.push( )
+ }
+
+ return
+ {pages}
+ setCnt(cnt + 1)}>Load More
+
+}
+```
+
+### Casos Avançados
+
+Entretanto, em alguns casos avançados, a solução acima não funciona.
+
+Por exemplo, nós ainda estamos implementando o mesmo "Carregar mais", mas também precisamos mostrar um número
+sobre quantos itens existem no total. Nós não podemos usar o ` ` novamente porque
+a top level UI (` `) precisa dos dados dentro de cada página:
+
+```jsx {10}
+function App () {
+ const [cnt, setCnt] = useState(1)
+
+ const pages = []
+ for (let i = 0; i < cnt; i++) {
+ pages.push( )
+ }
+
+ return
+
??? items
+ {pages}
+
setCnt(cnt + 1)}>Load More
+
+}
+```
+
+Também, se a API de paginação for **baseada em cursor**, essa solução não funciona também. Porque cada página
+precisa dos dados da página interior, elas não são isoladas.
+
+É assim que esse novo hook `useSWRInfinite` pode ajudar.
+
+## useSWRInfinite
+
+`useSWRInfinite` nos dá a habilidade de disparar uma quantidade de solicitações com um Hook. Isso é como se fosse:
+
+```jsx
+import useSWRInfinite from 'swr/infinite'
+
+// ...
+const { data, error, isLoading, isValidating, mutate, size, setSize } = useSWRInfinite(
+ getKey, fetcher?, options?
+)
+```
+
+Similar ao `useSWR`, esse novo Hook aceita uma função que retorna a chave de pedido, uma função de busca, e opções.
+Ele retorna todos os valores que `useSWR` retorna, incluindo 2 valores extras: o tamanho da página e um setter do tamanho da página, como um state do React.
+
+In infinite loading, one _page_ is one request, and our goal is to fetch multiple pages and render them.
+
+
+ Se você está usando versões SWR 0.x, `useSWRInfinite` precisa ser importado de `swr`:
+ `import { useSWRInfinite } from 'swr'`
+
+
+### API
+
+#### Parâmetros
+
+- `getKey`: uma função que aceita o índice e os dados da página anterior, retorna a chave de uma página
+- `fetcher`: mesma função que a [função fetcher](/docs/data-fetching) do `useSWR`.
+- `options`: aceita todas as opções que `useSWR` suporta, com 4 opções adicionais:
+ - `initialSize=1`: número de páginas devem ser carregadas inicialmente
+ - `revalidateAll=false`: sempre tentar revalidar todas as páginas
+ - `revalidateFirstPage=true`: sempre tentar revalidar a primeira página
+ - `persistSize=false`: não reseta o tamanho da página para 1 (ou `initialSize` se setado) quando a chave da primeira página muda
+
+
+ Note que a opção `initialSize` não é permitida a mudar no ciclo de vida.
+
+
+#### Valores de Retorno
+
+- `data`: um array de respostas de fetch para cada página
+- `error`: mesma que o objeto `error` do `useSWR`.
+- `isLoading`: mesmo que o `isLoading` do `useSWR`.
+- `isValidating`: mesmo que o `isValidating` do `useSWR`.
+- `mutate`: igual à função de mutação vinculada do `useSWR`, mas manipula o array de dados
+- `size`: o número de páginas que _vão_ ser carregadas e retornadas
+- `setSize`: define o número de páginas que precisam ser carregadas
+
+### Exemplo 1: API de Paginação Baseada em Índice
+
+Para APIs baseadas em índice:
+
+```plaintext
+GET /users?page=0&limit=10
+[
+ { name: 'Alice', ... },
+ { name: 'Bob', ... },
+ { name: 'Cathy', ... },
+ ...
+]
+```
+
+```jsx {4,5,6,7,10}
+// A função para obter a chave SWR de cada página,
+// o valor retornado será aceito pela função `fetcher`.
+// Se o valor `null` é retornado, a solicitação da página não iniciará.
+const getKey = (pageIndex, previousPageData) => {
+ if (previousPageData && !previousPageData.length) return null // atingiu o fim
+ return `/users?page=${pageIndex}&limit=10` // chave SWR
+}
+
+function App () {
+ const { data, size, setSize } = useSWRInfinite(getKey, fetcher)
+ if (!data) return 'loading'
+
+ // Agora podemos calcular o número total de usuários
+ let totalUsers = 0
+ for (let i = 0; i < data.length; i++) {
+ totalUsers += data[i].length
+ }
+
+ return
+
{totalUsers} users listed
+ {data.map((users, index) => {
+ // `data` é um array de respostas da API de cada página.
+ return users.map(user =>
{user.name}
)
+ })}
+
setSize(size + 1)}>Load More
+
+}
+```
+
+A função `getKey` é a diferença maior entre `useSWRInfinite` e `useSWR`.
+Ela aceita o índice da página atual, bem como os dados da página anterior.
+Então ambas (paginação baseada em índice e páginação baseada em cursor) podem ser suportadas.
+
+Também, `data` não é mais apenas uma resposta da API. É um array de respostas da API:
+
+```js
+// `data` se parecerá com isso
+[
+ [
+ { name: 'Alice', ... },
+ { name: 'Bob', ... },
+ { name: 'Cathy', ... },
+ ...
+ ],
+ [
+ { name: 'John', ... },
+ { name: 'Paul', ... },
+ { name: 'George', ... },
+ ...
+ ],
+ ...
+]
+```
+
+### Exemplo 2: API de Paginação Baseada em Cursor ou Offset
+
+Vamos dizer que a API agora requer um cursor e retorna o próximo cursor junto com os dados:
+
+```plaintext
+GET /users?cursor=123&limit=10
+{
+ data: [
+ { name: 'Alice' },
+ { name: 'Bob' },
+ { name: 'Cathy' },
+ ...
+ ],
+ nextCursor: 456
+}
+```
+
+Nós podemos mudar nossa função `getKey` para:
+
+```jsx
+const getKey = (pageIndex, previousPageData) => {
+ // alcançou o fim
+ if (previousPageData && !previousPageData.data) return null
+
+ // primeira página, nós não temos `previousPageData`
+ if (pageIndex === 0) return `/users?limit=10`
+
+ // adiciona o cursor para o endpoint da API
+ return `/users?cursor=${previousPageData.nextCursor}&limit=10`
+}
+```
+
+### Funcionalidades Avançadas
+
+[Aqui tem um exemplo](/examples/infinite-loading) mostrando como você pode implementar as seguintes funcionalidades com `useSWRInfinite`:
+
+- estados de carregamento
+- mostrar uma interface especial se estiver vazio
+- desativar o botão "Carregar mais" se alcançou o fim
+- fonte de dados alterável
+- atualizar toda a lista
diff --git a/pages/docs/pagination.ru.mdx b/pages/docs/pagination.ru.mdx
index 1a442330..b855c163 100644
--- a/pages/docs/pagination.ru.mdx
+++ b/pages/docs/pagination.ru.mdx
@@ -172,7 +172,7 @@ function App () {
import useSWRInfinite from 'swr/infinite'
// ...
-const { data, error, isValidating, mutate, size, setSize } = useSWRInfinite(
+const { data, error, isLoading, isValidating, mutate, size, setSize } = useSWRInfinite(
getKey, fetcher?, options?
)
```
@@ -207,6 +207,7 @@ const { data, error, isValidating, mutate, size, setSize } = useSWRInfinite(
- `data`: массив значений ответа выборки каждой страницы
- `error`: то же , что и `error` в `useSWR`
+- `isLoading`: то же, что и `isLoading` в `useSWR`
- `isValidating`: то же, что и `isValidating` в `useSWR`
- `mutate`: то же, что и связанная функция мутации в `useSWR`, но манипулирует массивом данных
- `size`: количество страниц, которые _будут_ извлекаться и возвращаться
diff --git a/pages/docs/pagination.zh-CN.mdx b/pages/docs/pagination.zh-CN.mdx
index 14912298..c55633e8 100644
--- a/pages/docs/pagination.zh-CN.mdx
+++ b/pages/docs/pagination.zh-CN.mdx
@@ -164,7 +164,7 @@ function App () {
import useSWRInfinite from 'swr/infinite'
// ...
-const { data, error, isValidating, mutate, size, setSize } = useSWRInfinite(
+const { data, error, isLoading, isValidating, mutate, size, setSize } = useSWRInfinite(
getKey, fetcher?, options?
)
```
@@ -198,6 +198,7 @@ const { data, error, isValidating, mutate, size, setSize } = useSWRInfinite(
- `data`: 一个数组,每个页面请求的响应值
- `error`: 与 `useSWR` 的 `error` 返回值相同
+- `isLoading`: 与 `useSWR` 的 `isLoading` 返回值相同
- `isValidating`: 与 `useSWR` 的 `isValidating` 返回值相同
- `mutate`: 和 `useSWR` 的绑定 mutate 函数一样,但是操作 data 数组
- `size`: *即将*请求和返回的页面数
diff --git a/pages/docs/prefetching.en-US.md b/pages/docs/prefetching.en-US.md
index 600ca77e..864eed3c 100644
--- a/pages/docs/prefetching.en-US.md
+++ b/pages/docs/prefetching.en-US.md
@@ -14,20 +14,88 @@ It will prefetch the data when the HTML loads, even before JavaScript starts to
## Programmatically Prefetch
-Sometimes, you want to preload a resource conditionally. For example, preloading the data when the user is [hovering](https://github.com/GoogleChromeLabs/quicklink) [a](https://github.com/guess-js/guess) [link](https://instant.page). The most intuitive way is to have a function to refetch and set the cache via the global [mutate](/docs/mutation):
+SWR provides the `preload` API to prefetch the resources programmatically and store the results in the cache. `preload` accepts `key` and `fetcher` as the arguments.
-```js
-import { mutate } from 'swr'
+You can call `preload` even outside of React.
-function prefetch () {
- mutate('/api/data', fetch('/api/data').then(res => res.json()))
- // the second parameter is a Promise
- // SWR will use the result when it resolves
+```jsx
+import { useState } from 'react'
+import useSWR, { preload } from 'swr'
+
+const fetcher = (url) => fetch(url).then((res) => res.json())
+
+// Preload the resource before rendering the User component below,
+// this prevents potential waterfalls in your application.
+// You can also start preloading when hovering the button or link, too.
+preload('/api/user', fetcher)
+
+function User() {
+ const { data } = useSWR('/api/user', fetcher)
+ ...
+}
+
+export default function App() {
+ const [show, setShow] = useState(false)
+ return (
+
+ setShow(true)}>Show User
+ {show ? : null}
+
+ )
+}
+```
+
+Within React rendering tree, `preload` is also avaiable to use in event handlers or effects.
+
+```jsx
+function App({ userId }) {
+ const [show, setShow] = useState(false)
+
+ // preload in effects
+ useEffect(() => {
+ preload('/api/user?id=' + userId, fetcher)
+ }, [useId])
+
+ return (
+
+ setShow(true)}
+ {/* preload in event callbacks */}
+ onHover={() => preload('/api/user?id=' + userId, fetcher)}
+ >
+ Show User
+
+ {show ? : null}
+
+ )
}
```
Together with techniques like [page prefetching](https://nextjs.org/docs/api-reference/next/router#routerprefetch) in Next.js, you will be able to load both next page and data instantly.
+In Suspense mode, you should utilize `preload` to avoid waterfall problems.
+
+```jsx
+import useSWR, { preload } from 'swr'
+
+// should call before rendering
+preload('/api/user', fetcher);
+preload('/api/movies', fetcher);
+
+const Page = () => {
+ // The below useSWR hooks will suspend the rendering, but the requests to `/api/user` and `/api/movies` have started by `preload` already,
+ // so the waterfall problem doesn't happen.
+ const { data: user } = useSWR('/api/user', fetcher, { suspense: true });
+ const { data: movies } = useSWR('/api/movies', fetcher, { suspense: true });
+ return (
+
+
+
+
+ );
+}
+```
+
## Pre-fill Data
If you want to pre-fill existing data into the SWR cache, you can use the `fallbackData` option. For example:
diff --git a/pages/docs/prefetching.es-ES.md b/pages/docs/prefetching.es-ES.md
index cd2f43e3..0c11b1f8 100644
--- a/pages/docs/prefetching.es-ES.md
+++ b/pages/docs/prefetching.es-ES.md
@@ -14,20 +14,88 @@ Se precargarán los datos cuando se cargue el HTML, incluso antes de que el Java
## Programmatically Prefetch
-A veces, se quiere precargar un recurso de forma condicional. Por ejemplo, precargar los datos cuando el usuario está [hovering](https://github.com/GoogleChromeLabs/quicklink) [a](https://github.com/guess-js/guess) [link](https://instant.page). La forma más intuitiva es tener una función que recupere y fije la caché a través de la función global [mutate](/docs/mutation):
+SWR provides the `preload` API to prefetch the resources programmatically and store the results in the cache. `preload` accepts `key` and `fetcher` as the arguments.
-```js
-import { mutate } from 'swr'
+You can call `preload` even outside of React.
-function prefetch () {
- mutate('/api/data', fetch('/api/data').then(res => res.json()))
- // el segundo parametró es una Promise
- // SWR utilizará el resultado cuando resuelva
+```jsx
+import { useState } from 'react'
+import useSWR, { preload } from 'swr'
+
+const fetcher = (url) => fetch(url).then((res) => res.json())
+
+// Preload the resource before rendering the User component below,
+// this prevents potential waterfalls in your application.
+// You can also start preloading when hovering the button or link, too.
+preload('/api/user', fetcher)
+
+function User() {
+ const { data } = useSWR('/api/user', fetcher)
+ ...
+}
+
+export default function App() {
+ const [show, setShow] = useState(false)
+ return (
+
+ setShow(true)}>Show User
+ {show ? : null}
+
+ )
+}
+```
+
+Within React rendering tree, `preload` is also avaiable to use in event handlers or effects.
+
+```jsx
+function App({ userId }) {
+ const [show, setShow] = useState(false)
+
+ // preload in effects
+ useEffect(() => {
+ preload('/api/user?id=' + userId, fetcher)
+ }, [useId])
+
+ return (
+
+ setShow(true)}
+ {/* preload in event callbacks */}
+ onHover={() => preload('/api/user?id=' + userId, fetcher)}
+ >
+ Show User
+
+ {show ? : null}
+
+ )
}
```
Junto con técnicas como [page prefetching](https://nextjs.org/docs/api-reference/next/router#routerprefetch) en Next.js, podrás cargar tanto la siguiente página como los datos al instante.
+In Suspense mode, you should utilize `preload` to avoid waterfall problems.
+
+```jsx
+import useSWR, { preload } from 'swr'
+
+// should call before rendering
+preload('/api/user', fetcher);
+preload('/api/movies', fetcher);
+
+const Page = () => {
+ // The below useSWR hooks will suspend the rendering, but the requests to `/api/user` and `/api/movies` have started by `preload` already,
+ // so the waterfall problem doesn't happen.
+ const { data: user } = useSWR('/api/user', fetcher, { suspense: true });
+ const { data: movies } = useSWR('/api/movies', fetcher, { suspense: true });
+ return (
+
+
+
+
+ );
+}
+```
+
## Pre-fill Data
If you want to pre-fill existing data into the SWR cache, you can use the `fallbackData` option. For example:
diff --git a/pages/docs/prefetching.ja.md b/pages/docs/prefetching.ja.md
index a6246923..d3b730ac 100644
--- a/pages/docs/prefetching.ja.md
+++ b/pages/docs/prefetching.ja.md
@@ -14,20 +14,88 @@ JavaScriptのダウンロードが開始される前であっても、HTMLの読
## プログラムによるプリフェッチ
-しばしば、リソースを条件付きでプリロードしたい場合があります。たとえば、ユーザーが [hovering](https://github.com/GoogleChromeLabs/quicklink) [a](https://github.com/guess-js/guess) [link](https://instant.page) にカーソルを合わせたときにデータをプリロードするような場合です。最も直観的な方法は、グローバルミューテートを使ってキャッシュを再取得し、設定する関数を作成することです。
+SWR は `preload` というデータをプログラマブルにプリフェッチして結果をキャッシュに保存する API を提供しています。`preload` は `key` と `fetcher` を引数として受け取ります。
-```js
-import { mutate } from 'swr'
+`preload` は React の外からも呼ぶことが可能です。
-function prefetch () {
- mutate('/api/data', fetch('/api/data').then(res => res.json()))
- // 2番目のパラメータはPromise
- // SWRは、その結果を解決する際に使用します。
+```jsx
+import { useState } from 'react'
+import useSWR, { preload } from 'swr'
+
+const fetcher = (url) => fetch(url).then((res) => res.json())
+
+// User コンポーネントがレンダリングされる前にプリロードします
+// これによりウォーターフォール問題の発生を避けられます
+// また、ボタンやリンクにホバーされたタイミングでプリロードを開始することもできます
+preload('/api/user', fetcher)
+
+function User() {
+ const { data } = useSWR('/api/user', fetcher)
+ ...
+}
+
+export default function App() {
+ const [show, setShow] = useState(false)
+ return (
+
+ setShow(true)}>Show User
+ {show ? : null}
+
+ )
+}
+```
+
+React のレンダリングツリー内においては, `preload` はイベントコールバックやエフェクトの中で利用可能です。
+
+```jsx
+function App({ userId }) {
+ const [show, setShow] = useState(false)
+
+ // エフェクトの中でプリロードする
+ useEffect(() => {
+ preload('/api/user?id=' + userId, fetcher)
+ }, [useId])
+
+ return (
+
+ setShow(true)}
+ {/* イベントコールバックの中でプリロードする */}
+ onHover={() => preload('/api/user?id=' + userId, fetcher)}
+ >
+ Show User
+
+ {show ? : null}
+
+ )
}
```
Next.js の [ページプリフェッチ](https://nextjs.org/docs/api-reference/next/router#routerprefetch) などの技術と合わせて、次のページとデータの両方を瞬時に読み込むことができるようになります。
+サスペンスモードでは特に `preload` を利用してウォーターフォール問題を避けた方がいいでしょう。
+
+```jsx
+import useSWR, { preload } from 'swr'
+
+// レンダリング前に呼ぶべき
+preload('/api/user', fetcher);
+preload('/api/movies', fetcher);
+
+const Page = () => {
+ // 下記の useSWR はレンダリングを中断しますが、`/api/user` と`/api/movies` に対するリクエストは `preload` によって開始されています
+ // そのため、ウォーターフォール問題は起きません
+ const { data: user } = useSWR('/api/user', fetcher, { suspense: true });
+ const { data: movies } = useSWR('/api/movies', fetcher, { suspense: true });
+ return (
+
+
+
+
+ );
+}
+```
+
## 事前データ
もし既に存在しているデータを SWR にキャッシュしたい場合は、 `fallbackData` オプションを使うことができます。たとえば:
diff --git a/pages/docs/prefetching.ko.md b/pages/docs/prefetching.ko.md
index 64ee8e24..4a9906f9 100644
--- a/pages/docs/prefetching.ko.md
+++ b/pages/docs/prefetching.ko.md
@@ -14,20 +14,88 @@ HTML `` 안에 넣기만 하면 됩니다. 쉽고 빠르며 네이티브
## 프로그래밍 방식으로 프리패치
-조건부로 리소스를 프리로드 하길 원할 수도 있습니다. 예를 들면, 사용자가 [어떤](https://github.com/guess-js/guess) [링크](https://instant.page)를 [호버링](https://github.com/GoogleChromeLabs/quicklink)할 때 데이터 프리로딩. 가장 직관적인 방법은 전역 [mutate](/docs/mutation)를 통해 캐시를 다시 가져오고 설정하는 함수를 두는 것입니다.
+SWR provides the `preload` API to prefetch the resources programmatically and store the results in the cache. `preload` accepts `key` and `fetcher` as the arguments.
-```js
-import { mutate } from 'swr'
+You can call `preload` even outside of React.
-function prefetch () {
- mutate('/api/data', fetch('/api/data').then(res => res.json()))
- // 두 번째 파라미터는 Promise입니다
- // 프로미스가 이행될 때 SWR은 그 결과를 사용합니다
+```jsx
+import { useState } from 'react'
+import useSWR, { preload } from 'swr'
+
+const fetcher = (url) => fetch(url).then((res) => res.json())
+
+// Preload the resource before rendering the User component below,
+// this prevents potential waterfalls in your application.
+// You can also start preloading when hovering the button or link, too.
+preload('/api/user', fetcher)
+
+function User() {
+ const { data } = useSWR('/api/user', fetcher)
+ ...
+}
+
+export default function App() {
+ const [show, setShow] = useState(false)
+ return (
+
+ setShow(true)}>Show User
+ {show ? : null}
+
+ )
+}
+```
+
+Within React rendering tree, `preload` is also avaiable to use in event handlers or effects.
+
+```jsx
+function App({ userId }) {
+ const [show, setShow] = useState(false)
+
+ // preload in effects
+ useEffect(() => {
+ preload('/api/user?id=' + userId, fetcher)
+ }, [useId])
+
+ return (
+
+ setShow(true)}
+ {/* preload in event callbacks */}
+ onHover={() => preload('/api/user?id=' + userId, fetcher)}
+ >
+ Show User
+
+ {show ? : null}
+
+ )
}
```
Next.js내의 [페이지 프리패칭](https://nextjs.org/docs/api-reference/next/router#routerprefetch)같은 기술과 함께 다음 페이지와 데이터 모두를 즉시 로드할 수 있습니다.
+In Suspense mode, you should utilize `preload` to avoid waterfall problems.
+
+```jsx
+import useSWR, { preload } from 'swr'
+
+// should call before rendering
+preload('/api/user', fetcher);
+preload('/api/movies', fetcher);
+
+const Page = () => {
+ // The below useSWR hooks will suspend the rendering, but the requests to `/api/user` and `/api/movies` have started by `preload` already,
+ // so the waterfall problem doesn't happen.
+ const { data: user } = useSWR('/api/user', fetcher, { suspense: true });
+ const { data: movies } = useSWR('/api/movies', fetcher, { suspense: true });
+ return (
+
+
+
+
+ );
+}
+```
+
## 데이터 프리필
이미 존재하는 데이터를 SWR 캐시에 미리 채우길 원한다면, `fallbackData` 옵션을 사용할 수 있습니다. 예를 들면:
diff --git a/pages/docs/prefetching.pt-BR.md b/pages/docs/prefetching.pt-BR.md
index a0324b83..640026d5 100644
--- a/pages/docs/prefetching.pt-BR.md
+++ b/pages/docs/prefetching.pt-BR.md
@@ -1,41 +1,109 @@
-# Pre-obtendo Dados
-
-## Dados de Página Top-Level
-
-Existem muitos jeitos de pre-obter os dados para SWR. Para requisições de página top-level, [`rel="preload"`](https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content) é altamente recomendado:
-
-```html
-
-```
-
-Basta apenas colocá-lo dentro do seu `` do HTML. É fácil, rápido e nativo.
-
-Irá pré-obter os dados quando o HTML carregar, antes mesmo de iniciar a baixar o JavaScript. Todos os seus pedidos de obtenção de dados com o mesmo URL vão usar o resultado (inclusive SWR, de modo que você pode usar o SWR para obter os dados de página top-level).
-
-## Prefetching Programático
-
-As vezes, você quer pré-carregar um recurso condicionalmente. Por exemplo, você quer pré-carregar os dados quando o usuário está [passando com o mouse](https://github.com/GoogleChromeLabs/quicklink) [sob](https://github.com/guess-js/guess) [um link](https://instant.page). A forma mais intuitiva é usar uma função para re-obter e definir o cache via [mutate](/docs/mutation) global:
-
-```js
-import { mutate } from 'swr'
-
-function prefetch () {
- mutate('/api/data', fetch('/api/data').then(res => res.json()))
- // o segundo parametro é uma Promise
- // SWR irá usar o resultado quando resolver
-}
-```
-
-Junto com técnicas como [page prefetching](https://nextjs.org/docs/api-reference/next/router#routerprefetch) no Next.js, você vai ser capaz de carregar ambos a próxima página e os dados instantaneamente.
-
-## Dados de Pré-preenchimento
-
-Se você quiser preencher dados existentes no cache do SWR, você pode usar a opção `fallbackData`. Por exemplo:
-
-```jsx
-useSWR('/api/data', fetcher, { fallbackData: prefetchedData })
-```
-
-Se o SWR ainda não obtiver os dados, este hook retornará `prefetchedData` como um fallback.
-
-Você pode também configurar isso para todos os hooks SWR e várias chaves com `` e a opção `fallback`. Veja [SSG e SSR com Next.js](/docs/with-nextjs) para mais detalhes.
+# Pre-obtendo Dados
+
+## Dados de Página Top-Level
+
+Existem muitos jeitos de pre-obter os dados para SWR. Para requisições de página top-level, [`rel="preload"`](https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content) é altamente recomendado:
+
+```html
+
+```
+
+Basta apenas colocá-lo dentro do seu `` do HTML. É fácil, rápido e nativo.
+
+Irá pré-obter os dados quando o HTML carregar, antes mesmo de iniciar a baixar o JavaScript. Todos os seus pedidos de obtenção de dados com o mesmo URL vão usar o resultado (inclusive SWR, de modo que você pode usar o SWR para obter os dados de página top-level).
+
+## Prefetching Programático
+
+SWR provides the `preload` API to prefetch the resources programmatically and store the results in the cache. `preload` accepts `key` and `fetcher` as the arguments.
+
+You can call `preload` even outside of React.
+
+```jsx
+import { useState } from 'react'
+import useSWR, { preload } from 'swr'
+
+const fetcher = (url) => fetch(url).then((res) => res.json())
+
+// Preload the resource before rendering the User component below,
+// this prevents potential waterfalls in your application.
+// You can also start preloading when hovering the button or link, too.
+preload('/api/user', fetcher)
+
+function User() {
+ const { data } = useSWR('/api/user', fetcher)
+ ...
+}
+
+export default function App() {
+ const [show, setShow] = useState(false)
+ return (
+
+ setShow(true)}>Show User
+ {show ? : null}
+
+ )
+}
+```
+
+Within React rendering tree, `preload` is also avaiable to use in event handlers or effects.
+
+```jsx
+function App({ userId }) {
+ const [show, setShow] = useState(false)
+
+ // preload in effects
+ useEffect(() => {
+ preload('/api/user?id=' + userId, fetcher)
+ }, [useId])
+
+ return (
+
+ setShow(true)}
+ {/* preload in event callbacks */}
+ onHover={() => preload('/api/user?id=' + userId, fetcher)}
+ >
+ Show User
+
+ {show ? : null}
+
+ )
+}
+```
+
+Junto com técnicas como [page prefetching](https://nextjs.org/docs/api-reference/next/router#routerprefetch) no Next.js, você vai ser capaz de carregar ambos a próxima página e os dados instantaneamente.
+
+In Suspense mode, you should utilize `preload` to avoid waterfall problems.
+
+```jsx
+import useSWR, { preload } from 'swr'
+
+// should call before rendering
+preload('/api/user', fetcher);
+preload('/api/movies', fetcher);
+
+const Page = () => {
+ // The below useSWR hooks will suspend the rendering, but the requests to `/api/user` and `/api/movies` have started by `preload` already,
+ // so the waterfall problem doesn't happen.
+ const { data: user } = useSWR('/api/user', fetcher, { suspense: true });
+ const { data: movies } = useSWR('/api/movies', fetcher, { suspense: true });
+ return (
+
+
+
+
+ );
+}
+```
+
+## Dados de Pré-preenchimento
+
+Se você quiser preencher dados existentes no cache do SWR, você pode usar a opção `fallbackData`. Por exemplo:
+
+```jsx
+useSWR('/api/data', fetcher, { fallbackData: prefetchedData })
+```
+
+Se o SWR ainda não obtiver os dados, este hook retornará `prefetchedData` como um fallback.
+
+Você pode também configurar isso para todos os hooks SWR e várias chaves com `` e a opção `fallback`. Veja [SSG e SSR com Next.js](/docs/with-nextjs) para mais detalhes.
diff --git a/pages/docs/prefetching.ru.md b/pages/docs/prefetching.ru.md
index b04364b1..4ff71887 100644
--- a/pages/docs/prefetching.ru.md
+++ b/pages/docs/prefetching.ru.md
@@ -14,20 +14,88 @@
## Программная предварительная выборка
-Иногда вы хотите предварительно загрузить ресурс условно. Например, предварительная загрузка данных, когда пользователь [наводит](https://github.com/GoogleChromeLabs/quicklink) [курсор](https://github.com/guess-js/guess) [на ссылку](https://instant.page). Самый интуитивно понятный способ — иметь функцию для повторной выборки и установки кеша с помощью глобального [mutate](/docs/mutation):
+SWR provides the `preload` API to prefetch the resources programmatically and store the results in the cache. `preload` accepts `key` and `fetcher` as the arguments.
-```js
-import { mutate } from 'swr'
+You can call `preload` even outside of React.
-function prefetch () {
- mutate('/api/data', fetch('/api/data').then(res => res.json()))
- // вторым параметром является промис
- // SWR использует его результат, после того, как он разрешится
+```jsx
+import { useState } from 'react'
+import useSWR, { preload } from 'swr'
+
+const fetcher = (url) => fetch(url).then((res) => res.json())
+
+// Preload the resource before rendering the User component below,
+// this prevents potential waterfalls in your application.
+// You can also start preloading when hovering the button or link, too.
+preload('/api/user', fetcher)
+
+function User() {
+ const { data } = useSWR('/api/user', fetcher)
+ ...
+}
+
+export default function App() {
+ const [show, setShow] = useState(false)
+ return (
+
+ setShow(true)}>Show User
+ {show ? : null}
+
+ )
+}
+```
+
+Within React rendering tree, `preload` is also avaiable to use in event handlers or effects.
+
+```jsx
+function App({ userId }) {
+ const [show, setShow] = useState(false)
+
+ // preload in effects
+ useEffect(() => {
+ preload('/api/user?id=' + userId, fetcher)
+ }, [useId])
+
+ return (
+
+ setShow(true)}
+ {/* preload in event callbacks */}
+ onHover={() => preload('/api/user?id=' + userId, fetcher)}
+ >
+ Show User
+
+ {show ? : null}
+
+ )
}
```
Вместе с такими техниками, как [предзагрузка страниц](https://nextjs.org/docs/api-reference/next/router#routerprefetch) в Next.js, вы сможете мгновенно загружать как следующую страницу, так и данные.
+In Suspense mode, you should utilize `preload` to avoid waterfall problems.
+
+```jsx
+import useSWR, { preload } from 'swr'
+
+// should call before rendering
+preload('/api/user', fetcher);
+preload('/api/movies', fetcher);
+
+const Page = () => {
+ // The below useSWR hooks will suspend the rendering, but the requests to `/api/user` and `/api/movies` have started by `preload` already,
+ // so the waterfall problem doesn't happen.
+ const { data: user } = useSWR('/api/user', fetcher, { suspense: true });
+ const { data: movies } = useSWR('/api/movies', fetcher, { suspense: true });
+ return (
+
+
+
+
+ );
+}
+```
+
## Предварительное заполнение данных
Если вы хотите предварительно заполнить существующие данные в кэш SWR, вы можете использовать опцию `fallbackData`. Например:
diff --git a/pages/docs/prefetching.zh-CN.md b/pages/docs/prefetching.zh-CN.md
index 420bbe94..3db98319 100644
--- a/pages/docs/prefetching.zh-CN.md
+++ b/pages/docs/prefetching.zh-CN.md
@@ -14,22 +14,88 @@
## 手动预请求
-有时,你想有条件地预加载资源。比如当用户 [hovering](https://github.com/GoogleChromeLabs/quicklink) [a](https://github.com/guess-js/guess) [link](https://instant.page) 时预加载数据。最直观的方法就是用全局的 [mutate](/docs/mutation) 重新请求和设置缓存:
+SWR provides the `preload` API to prefetch the resources programmatically and store the results in the cache. `preload` accepts `key` and `fetcher` as the arguments.
-另一个选择是有条件地预请求数据。你可以通过 [mutate](/docs/mutation) 来重新请求以及设置缓存:
+You can call `preload` even outside of React.
-```js
-import { mutate } from 'swr'
+```jsx
+import { useState } from 'react'
+import useSWR, { preload } from 'swr'
+
+const fetcher = (url) => fetch(url).then((res) => res.json())
+
+// Preload the resource before rendering the User component below,
+// this prevents potential waterfalls in your application.
+// You can also start preloading when hovering the button or link, too.
+preload('/api/user', fetcher)
+
+function User() {
+ const { data } = useSWR('/api/user', fetcher)
+ ...
+}
-function prefetch () {
- mutate('/api/data', fetch('/api/data').then(res => res.json()))
- // 第二个参数是个 Promise
- // SWR 将在解析时使用结果
+export default function App() {
+ const [show, setShow] = useState(false)
+ return (
+
+ setShow(true)}>Show User
+ {show ? : null}
+
+ )
+}
+```
+
+Within React rendering tree, `preload` is also avaiable to use in event handlers or effects.
+
+```jsx
+function App({ userId }) {
+ const [show, setShow] = useState(false)
+
+ // preload in effects
+ useEffect(() => {
+ preload('/api/user?id=' + userId, fetcher)
+ }, [useId])
+
+ return (
+
+ setShow(true)}
+ {/* preload in event callbacks */}
+ onHover={() => preload('/api/user?id=' + userId, fetcher)}
+ >
+ Show User
+
+ {show ? : null}
+
+ )
}
```
配合 Next.js 的 [页面预加载](https://nextjs.org/docs/api-reference/next/router#routerprefetch),你将能立即加载下一页和数据。
+In Suspense mode, you should utilize `preload` to avoid waterfall problems.
+
+```jsx
+import useSWR, { preload } from 'swr'
+
+// should call before rendering
+preload('/api/user', fetcher);
+preload('/api/movies', fetcher);
+
+const Page = () => {
+ // The below useSWR hooks will suspend the rendering, but the requests to `/api/user` and `/api/movies` have started by `preload` already,
+ // so the waterfall problem doesn't happen.
+ const { data: user } = useSWR('/api/user', fetcher, { suspense: true });
+ const { data: movies } = useSWR('/api/movies', fetcher, { suspense: true });
+ return (
+
+
+
+
+ );
+}
+```
+
## 数据预填充
如果你想在 SWR 缓存中预填充已经存在的数据,你可以使用 `fallbackData` 选项,例如:
diff --git a/pages/docs/revalidation.en-US.mdx b/pages/docs/revalidation.en-US.mdx
index 09c6450d..32dc8867 100644
--- a/pages/docs/revalidation.en-US.mdx
+++ b/pages/docs/revalidation.en-US.mdx
@@ -23,7 +23,7 @@ This can be useful to immediately synchronize to the latest state. This is helpf
/>
-This feature is enabled by default. You can disable it via the [`revalidateOnFocus`](/docs/options) option.
+This feature is enabled by default. You can disable it via the [`revalidateOnFocus`](/docs/api) option.
## Revalidate on Interval
@@ -39,7 +39,7 @@ SWR will give you the option to automatically refetch data. It’s **smart** whi
/>
-You can enable it by setting a [`refreshInterval`](/docs/options) value:
+You can enable it by setting a [`refreshInterval`](/docs/api) value:
```js
useSWR('/api/todos', fetcher, { refreshInterval: 1000 })
@@ -53,7 +53,7 @@ It's useful to also revalidate when the user is back online. This scenario happe
To make sure the data is always up-to-date, SWR automatically revalidates when network recovers.
-This feature is enabled by default. You can disable it via the [`revalidateOnReconnect`](/docs/options) option.
+This feature is enabled by default. You can disable it via the [`revalidateOnReconnect`](/docs/api) option.
## Disable Automatic Revalidations
diff --git a/pages/docs/revalidation.es-ES.mdx b/pages/docs/revalidation.es-ES.mdx
index a3d138c0..761f7f4c 100644
--- a/pages/docs/revalidation.es-ES.mdx
+++ b/pages/docs/revalidation.es-ES.mdx
@@ -24,7 +24,7 @@ como stale mobile tabs, o laptops que están **suspendida**.
/>
-Esta caracteristica está activada por defecto. Puede desactivarla mediante de la opción [`revalidateOnFocus`](/docs/options).
+Esta caracteristica está activada por defecto. Puede desactivarla mediante de la opción [`revalidateOnFocus`](/docs/api).
## Revalidar on interval
@@ -42,7 +42,7 @@ componente asociado al hook está **en la pantalla**.
/>
-Puede activarlo estableciendo un valor de [`refreshInterval`](/docs/options).
+Puede activarlo estableciendo un valor de [`refreshInterval`](/docs/api).
```js
useSWR('/api/todos', fetcher, { refreshInterval: 1000 })
@@ -58,7 +58,7 @@ Este escenario se da con frecuencia cuando el usuario desbloquea su ordenador, p
Para asegurarse de que los datos están siempre actualizados, SWR revalida automáticamente cuando la red se recupera.
-Esta función está activada por defecto. Puede desactivarla mediante la opción [`revalidateOnReconnect`](/docs/options).
+Esta función está activada por defecto. Puede desactivarla mediante la opción [`revalidateOnReconnect`](/docs/api).
## Disable Automatic Revalidations
diff --git a/pages/docs/revalidation.ja.mdx b/pages/docs/revalidation.ja.mdx
index 9e1f0f61..2ff1208f 100644
--- a/pages/docs/revalidation.ja.mdx
+++ b/pages/docs/revalidation.ja.mdx
@@ -23,7 +23,7 @@ import Link from 'next/link'
/>
-この機能はデフォルトで有効になっています。[`revalidateOnFocus`](/docs/options) オプションで無効にできます。
+この機能はデフォルトで有効になっています。[`revalidateOnFocus`](/docs/api) オプションで無効にできます。
## 定期的な再検証
@@ -39,7 +39,7 @@ SWR には、データを自動的に再フェッチするオプションがあ
/>
-[`refreshInterval`](/docs/options) 値を設定することで有効にできます:
+[`refreshInterval`](/docs/api) 値を設定することで有効にできます:
```js
useSWR('/api/todos', fetcher, { refreshInterval: 1000 })
@@ -53,7 +53,7 @@ useSWR('/api/todos', fetcher, { refreshInterval: 1000 })
データが常に最新であることを確認するために、SWR はネットワークが回復したときに自動的に再検証します。
-この機能はデフォルトで有効になっています。[`revalidateOnReconnect`](/docs/options) オプションで無効にできます。
+この機能はデフォルトで有効になっています。[`revalidateOnReconnect`](/docs/api) オプションで無効にできます。
## 自動再検証の無効化
diff --git a/pages/docs/revalidation.ko.mdx b/pages/docs/revalidation.ko.mdx
index 01505dd6..4e48bd35 100644
--- a/pages/docs/revalidation.ko.mdx
+++ b/pages/docs/revalidation.ko.mdx
@@ -23,7 +23,7 @@ import Link from 'next/link'
/>
-이 기능은 기본적으로 활성화되어 있습니다. [`revalidateOnFocus`](/docs/options) 옵션을 통해 이를 비활성화할 수 있습니다.
+이 기능은 기본적으로 활성화되어 있습니다. [`revalidateOnFocus`](/docs/api) 옵션을 통해 이를 비활성화할 수 있습니다.
## 인터벌 시에 갱신하기
@@ -39,7 +39,7 @@ SWR은 자동 데이터 다시 가져오기 옵션을 제공합니다. hook과
/>
-[`refreshInterval`](/docs/options) 값을 설정하여 활성화할 수 있습니다.
+[`refreshInterval`](/docs/api) 값을 설정하여 활성화할 수 있습니다.
```js
useSWR('/api/todos', fetcher, { refreshInterval: 1000 })
@@ -53,7 +53,7 @@ useSWR('/api/todos', fetcher, { refreshInterval: 1000 })
데이터를 항상 최신으로 보장하기 위해 네트워크가 회복될 때 SWR은 자동으로 갱신합니다.
-이 기능은 기본적으로 활성화되어 있습니다. [`revalidateOnReconnect`](/docs/options) 옵션을 통해 비활성화할 수 있습니다.
+이 기능은 기본적으로 활성화되어 있습니다. [`revalidateOnReconnect`](/docs/api) 옵션을 통해 비활성화할 수 있습니다.
## 자동 갱신 비활성화하기
diff --git a/pages/docs/revalidation.pt-BR.mdx b/pages/docs/revalidation.pt-BR.mdx
index 9e57820a..558d7d13 100644
--- a/pages/docs/revalidation.pt-BR.mdx
+++ b/pages/docs/revalidation.pt-BR.mdx
@@ -1,86 +1,86 @@
-import Video from 'components/video'
-import Callout from 'nextra-theme-docs/callout'
-import Bleed from 'nextra-theme-docs/bleed'
-import Link from 'next/link'
-
-# Revalidação Automática
-
-
- Se você quer validar manualmente os dados, verifique Mutação .
-
-
-## Revalidar ao Focar
-
-Quando você focar novamente ou trocar de aba, SWR revalidará automaticamente os dados.
-
-Isso pode ser útil para sincronizar imediatamente para o último estado. Isso é útil em situações como abas de celular obsoletas, ou laptops que **ficaram em modo descanso**.
-
-
-
-
-
-Essa funcionalidade é habilitada por padrão. Você pode desabilitá-la pela opção [`revalidateOnFocus`](/docs/options).
-
-## Revalidar em Intervalos
-
-Em vários casos, os dados mudam graças a diversos dispositivos, diversos usuários, diversas abas. Como podemos atualizar o estado de forma dinâmica?
-
-SWR lhe dará a opção de revalidar automaticamente. É **inteligente**, o que significa que o refetch só ocorrerá se o componente associado ao hook estiver **visível em tela**.
-
-
-
-
-
-Você pode habilitá-lo definindo um valor para [`refreshInterval`](/docs/options):
-
-```js
-useSWR('/api/todos', fetcher, { refreshInterval: 1000 })
-```
-
-Existem também opções como `refreshWhenHidden` e `refreshWhenOffline`. Ambas são desabilitados por padrão, então SWR não fará fetch quando a página não estiver na tela, ou não houver conexão com a internet.
-
-## Revalidar ao Reconectar
-
-É útil também revalidar quando o usuário volta a ter uma conexão. Esse cenário acontece muitas vezes quando o usuário desbloqueia o computador, mas a internet ainda não está conectada no momento.
-
-Para ter certeza que os dados estão sempre atualizados, SWR revalidará automaticamente quando a rede for recuperada.
-
-Essa funcionalidade é habilitada por padrão. Você pode desabilitá-la pela opção [`revalidateOnReconnect`](/docs/options).
-
-## Desabilitar Revalidações Automáticas
-
-Se o recurso é **imutável**, que nunca mudará se revalidarmos novamente, podemos desabilitar todas as revalidações automáticas para ele.
-
-Desde a versão 1.0, SWR fornece um hook `useSWRImmutable` para marcar o recurso como imutável:
-
-```js
-import useSWRImmutable from 'swr/immutable'
-
-// ...
-useSWRImmutable(key, fetcher, options)
-```
-
-Ele possui a mesma interface de API do hook `useSWR` normal. Você também pode fazer o mesmo desabilitando as seguintes opções de revalidação:
-
-```js
-useSWR(key, fetcher, {
- revalidateIfStale: false,
- revalidateOnFocus: false,
- revalidateOnReconnect: false
-})
-
-// equivalente a
-useSWRImmutable(key, fetcher)
-```
-
-O `revalidateIfStale` controla se SWR deve revalidar quando ele é montado e há dados desatualizados.
-
-Esses 2 hooks acima fazem o **mesma** coisa. Uma vez que os dados são armazenados, eles nunca mais pedirão para obtê-los novamente.
+import Video from 'components/video'
+import Callout from 'nextra-theme-docs/callout'
+import Bleed from 'nextra-theme-docs/bleed'
+import Link from 'next/link'
+
+# Revalidação Automática
+
+
+ Se você quer validar manualmente os dados, verifique Mutação .
+
+
+## Revalidar ao Focar
+
+Quando você focar novamente ou trocar de aba, SWR revalidará automaticamente os dados.
+
+Isso pode ser útil para sincronizar imediatamente para o último estado. Isso é útil em situações como abas de celular obsoletas, ou laptops que **ficaram em modo descanso**.
+
+
+
+
+
+Essa funcionalidade é habilitada por padrão. Você pode desabilitá-la pela opção [`revalidateOnFocus`](/docs/api).
+
+## Revalidar em Intervalos
+
+Em vários casos, os dados mudam graças a diversos dispositivos, diversos usuários, diversas abas. Como podemos atualizar o estado de forma dinâmica?
+
+SWR lhe dará a opção de revalidar automaticamente. É **inteligente**, o que significa que o refetch só ocorrerá se o componente associado ao hook estiver **visível em tela**.
+
+
+
+
+
+Você pode habilitá-lo definindo um valor para [`refreshInterval`](/docs/api):
+
+```js
+useSWR('/api/todos', fetcher, { refreshInterval: 1000 })
+```
+
+Existem também opções como `refreshWhenHidden` e `refreshWhenOffline`. Ambas são desabilitados por padrão, então SWR não fará fetch quando a página não estiver na tela, ou não houver conexão com a internet.
+
+## Revalidar ao Reconectar
+
+É útil também revalidar quando o usuário volta a ter uma conexão. Esse cenário acontece muitas vezes quando o usuário desbloqueia o computador, mas a internet ainda não está conectada no momento.
+
+Para ter certeza que os dados estão sempre atualizados, SWR revalidará automaticamente quando a rede for recuperada.
+
+Essa funcionalidade é habilitada por padrão. Você pode desabilitá-la pela opção [`revalidateOnReconnect`](/docs/api).
+
+## Desabilitar Revalidações Automáticas
+
+Se o recurso é **imutável**, que nunca mudará se revalidarmos novamente, podemos desabilitar todas as revalidações automáticas para ele.
+
+Desde a versão 1.0, SWR fornece um hook `useSWRImmutable` para marcar o recurso como imutável:
+
+```js
+import useSWRImmutable from 'swr/immutable'
+
+// ...
+useSWRImmutable(key, fetcher, options)
+```
+
+Ele possui a mesma interface de API do hook `useSWR` normal. Você também pode fazer o mesmo desabilitando as seguintes opções de revalidação:
+
+```js
+useSWR(key, fetcher, {
+ revalidateIfStale: false,
+ revalidateOnFocus: false,
+ revalidateOnReconnect: false
+})
+
+// equivalente a
+useSWRImmutable(key, fetcher)
+```
+
+O `revalidateIfStale` controla se SWR deve revalidar quando ele é montado e há dados desatualizados.
+
+Esses 2 hooks acima fazem o **mesma** coisa. Uma vez que os dados são armazenados, eles nunca mais pedirão para obtê-los novamente.
diff --git a/pages/docs/revalidation.ru.mdx b/pages/docs/revalidation.ru.mdx
index 35256340..d399aeeb 100644
--- a/pages/docs/revalidation.ru.mdx
+++ b/pages/docs/revalidation.ru.mdx
@@ -23,7 +23,7 @@ import Link from 'next/link'
/>
-Эта функция включена по умолчанию. Вы можете её отключить через опцию [`revalidateOnFocus`](/docs/options).
+Эта функция включена по умолчанию. Вы можете её отключить через опцию [`revalidateOnFocus`](/docs/api).
## Ревалидация с интервалом
@@ -39,7 +39,7 @@ SWR даст вам возможность автоматически обнов
/>
-Вы можете включить её, установив значение для [`refreshInterval`](/docs/options):
+Вы можете включить её, установив значение для [`refreshInterval`](/docs/api):
```js
useSWR('/api/todos', fetcher, { refreshInterval: 1000 })
@@ -54,7 +54,7 @@ useSWR('/api/todos', fetcher, { refreshInterval: 1000 })
Чтобы данные всегда были актуальными, SWR автоматически делает ревалидацию при восстановлении сети.
-Эта функция включена по умолчанию. Вы можете её отключить через опцию [`revalidateOnReconnect`](/docs/options).
+Эта функция включена по умолчанию. Вы можете её отключить через опцию [`revalidateOnReconnect`](/docs/api).
## Отключение автоматических ревалидаций
diff --git a/pages/docs/revalidation.zh-CN.mdx b/pages/docs/revalidation.zh-CN.mdx
index a385c78c..d68e12f0 100644
--- a/pages/docs/revalidation.zh-CN.mdx
+++ b/pages/docs/revalidation.zh-CN.mdx
@@ -23,7 +23,7 @@ import Link from 'next/link'
/>
-该特性默认是启用的。你可以通过 [`revalidateOnFocus`](/docs/options) 选项禁用它。
+该特性默认是启用的。你可以通过 [`revalidateOnFocus`](/docs/api) 选项禁用它。
## 定期重新请求
@@ -39,7 +39,7 @@ SWR 会为你提供自动重新请求数据的选项。这很 **智能**,意
/>
-你可以通过设置 [`refreshInterval`](/docs/options) 值来启用它:
+你可以通过设置 [`refreshInterval`](/docs/api) 值来启用它:
```js
useSWR('/api/todos', fetcher, { refreshInterval: 1000 })
@@ -53,7 +53,7 @@ useSWR('/api/todos', fetcher, { refreshInterval: 1000 })
为了确保数据始终是最新的,SWR 会在网络恢复时自动重新请求。
-该特性默认是启用的。你可以通过 [`revalidateOnReconnect`](/docs/options) 选项禁用它。
+该特性默认是启用的。你可以通过 [`revalidateOnReconnect`](/docs/api) 选项禁用它。
## 禁用自动重新请求
diff --git a/pages/docs/suspense.en-US.mdx b/pages/docs/suspense.en-US.mdx
index 43ad9a03..f76d2bc5 100644
--- a/pages/docs/suspense.en-US.mdx
+++ b/pages/docs/suspense.en-US.mdx
@@ -3,12 +3,7 @@ import Callout from 'nextra-theme-docs/callout'
# Suspense
- Suspense is currently an experimental feature of React. These APIs may change significantly and without a warning before they become a part of React.
- More information
-
-
-
- Note that React Suspense is not yet supported in SSR mode.
+ React still doesn't recommend using `Suspense` in data frameworks like SWR (More information ). These APIs may change in the future as the results of our research.
You can enable the `suspense` option to use SWR with React Suspense:
@@ -46,6 +41,10 @@ But if an error occurred, you need to use an [error boundary](https://reactjs.or
```
+
+Suspense mode suspends rendering until the data is ready, which means it causes waterfall problems easily. To avoid that, you should prefetch resources before rendering. [More information](/docs/prefetching)
+
+
---
### Note: With Conditional Fetching
@@ -73,3 +72,7 @@ function Profile () {
```
If you want to read more technical details about this restriction, check [the discussion here](https://github.com/vercel/swr/pull/357#issuecomment-627089889).
+
+### Server-Side Rendering
+
+When using suspense mode on the server-side (including pre-rendering in Next.js), it's **required** to provide the initial data via [fallbackData or fallback](/docs/with-nextjs#pre-rendering-with-default-data). This means that you can't use `Suspense` to fetch data on the server side, but either doing fully client-side data fetching, or fetch the data via the framework level data fetching method(such as getStaticProps in Next.js). More discussions can be found [here](https://github.com/vercel/swr/issues/1906).
diff --git a/pages/docs/suspense.es-ES.mdx b/pages/docs/suspense.es-ES.mdx
index 3533a382..56a89709 100644
--- a/pages/docs/suspense.es-ES.mdx
+++ b/pages/docs/suspense.es-ES.mdx
@@ -3,12 +3,7 @@ import Callout from 'nextra-theme-docs/callout'
# Suspense
- Suspense es actualmente una característica experimental de React. Estas APIs pueden cambiar significativamente y sin previo aviso antes de que se conviertan en parte de React.
- Más información
-
-
-
- Tenga en cuenta que React Suspense aún no es compatible con el modo SSR.
+ React still doesn't recommend using `Suspense` in data frameworks like SWR (More information ). These APIs may change in the future as the results of our research.
Puede activar la opción `suspense` para utilizar SWR con React Suspense:
@@ -46,6 +41,10 @@ Pero si se produce un error, es necesario utilizar un [error boundary](https://r
```
+
+Suspense mode suspends rendering until the data is ready, which means it causes waterfall problems easily. To avoid that, you should prefetch resources before rendering. [More information](/docs/prefetching)
+
+
---
### Note: With Conditional Fetching
@@ -73,3 +72,7 @@ function Profile () {
```
Si quiere leer más detalles técnicos sobre esta restricción, consulte [la discusión aquí](https://github.com/vercel/swr/pull/357#issuecomment-627089889).
+
+### Server-Side Rendering
+
+When using suspense mode on the server-side (including pre-rendering in Next.js), it's **required** to provide the initial data via [fallbackData or fallback](/docs/with-nextjs#pre-rendering-with-default-data). This means that you can't use `Suspense` to fetch data on the server side, but either doing fully client-side data fetching, or fetch the data via the framework level data fetching method(such as getStaticProps in Next.js). More discussions can be found [here](https://github.com/vercel/swr/issues/1906).
diff --git a/pages/docs/suspense.ja.mdx b/pages/docs/suspense.ja.mdx
index ecb06d3b..747a5fb0 100644
--- a/pages/docs/suspense.ja.mdx
+++ b/pages/docs/suspense.ja.mdx
@@ -3,12 +3,7 @@ import Callout from 'nextra-theme-docs/callout'
# サスペンス
- サスペンスは現在、React の実験的な 機能です。これらの API は、React の一部になる前に、警告なしに大幅に変更される可能性があります。
- 詳しい情報
-
-
-
- React サスペンスは SSR モードではまだサポートされていないことに注意してください。
+ React はまだサスペンスをデータ取得フレームワークである SWR などで使うことを 推奨していません (詳細 )。 これらの API は将来的に私たちの調査により変更される可能性があります。
React サスペンスで SWR を使用するには、 `suspense` オプションを有効にします。
@@ -46,6 +41,10 @@ function App () {
```
+
+サスペンスモードはデータが利用可能になるまでレンダリングを中断します。これは簡単にウォーターフォール問題を引き起こすことを意味します。これを避けるためにはレンダリングの前にリソースをプリフェッチする必要があります。[詳細はこちら](/docs/prefetching)
+
+
---
### 注:条件付きフェッチを使用する場合
@@ -73,3 +72,7 @@ function Profile () {
```
この制限に関する技術的な詳細を読みたい場合は、[こちらの議論](https://github.com/vercel/swr/pull/357#issuecomment-627089889)を確認してください。
+
+### サーバサイドレンダリング
+
+サスペンスモードをサーバサイド(Next.js によるプリレンダリングを含む)で使う場合、[fallbackData や fallback](/docs/with-nextjs#pre-rendering-with-default-data) により初期データが提供されている**必要があります**。これはサスペンスをサーバサイドでのデータ取得に使うことができないことを意味しており、クライアントサイドでのデータ取得を行うか、フレームワークを通じたデータ取得(Next.js の getStaticProps のような)のどちらかを行う必要があります。これについての議論は[こちら](https://github.com/vercel/swr/issues/1906)です。
diff --git a/pages/docs/suspense.ko.mdx b/pages/docs/suspense.ko.mdx
index dd80d27a..ece76fca 100644
--- a/pages/docs/suspense.ko.mdx
+++ b/pages/docs/suspense.ko.mdx
@@ -3,12 +3,7 @@ import Callout from 'nextra-theme-docs/callout'
# 서스펜스
- 서스펜스는 현재 React의 실험적인 기능입니다. 이러한 API는 React의 일부가 되기 전에 경고 없이 크게 변경될 수 있습니다.
- 더 상세한 정보
-
-
-
- React 서스펜스는 SSR 모드를 아직 지원하지 않습니다.
+ React still doesn't recommend using `Suspense` in data frameworks like SWR (More information ). These APIs may change in the future as the results of our research.
React 서스펜스를 SWR과 함께 사용하려면 `suspense` 옵션을 활성화하세요.
@@ -46,6 +41,10 @@ function App () {
```
+
+Suspense mode suspends rendering until the data is ready, which means it causes waterfall problems easily. To avoid that, you should prefetch resources before rendering. [More information](/docs/prefetching)
+
+
---
### 노트: 조건부 가져오기와 함께
@@ -73,3 +72,7 @@ function Profile () {
```
이 제한에 대한 더 기술적인 상세 내용을 읽으려면 [여기 discussion](https://github.com/vercel/swr/pull/357#issuecomment-627089889)을 확인해 보세요.
+
+### Server-Side Rendering
+
+When using suspense mode on the server-side (including pre-rendering in Next.js), it's **required** to provide the initial data via [fallbackData or fallback](/docs/with-nextjs#pre-rendering-with-default-data). This means that you can't use `Suspense` to fetch data on the server side, but either doing fully client-side data fetching, or fetch the data via the framework level data fetching method(such as getStaticProps in Next.js). More discussions can be found [here](https://github.com/vercel/swr/issues/1906).
diff --git a/pages/docs/suspense.pt-BR.mdx b/pages/docs/suspense.pt-BR.mdx
index e10dae8f..f8f2c86e 100644
--- a/pages/docs/suspense.pt-BR.mdx
+++ b/pages/docs/suspense.pt-BR.mdx
@@ -1,75 +1,76 @@
-import Callout from 'nextra-theme-docs/callout'
-
-# Suspense
-
-
- Suspense é atualmente uma funcionalidade experimental do React. Estas APIs podem mudar significativamente e sem aviso antes de serem adicionadas ao React.
- Mais informações
-
-
-
- Note que o React Suspense não é suportado no modo SSR.
-
-
-Você pode habilitar a opção `suspense` para usar SWR com React Suspense:
-
-```jsx
-import { Suspense } from 'react'
-import useSWR from 'swr'
-
-function Profile () {
- const { data } = useSWR('/api/user', fetcher, { suspense: true })
- return hello, {data.name}
-}
-
-function App () {
- return (
- loading... }>
-
-
- )
-}
-```
-
-
- Note que a opção `suspense` não é permitida a mudar no ciclo de vida.
-
-
-Em modo Suspense, `data` é sempre a resposta do fetch (então você não precisa checar se é `undefined`).
-Mas se um erro ocorreu, você precisa usar um [limite de erro (Error Boundary)](https://pt-br.reactjs.org/docs/error-boundaries.html) para o capturar:
-
-```jsx
-Could not fetch posts.}>
- Loading posts...}>
-
-
-
-```
-
----
-
-### Nota: Com Fetching Condicional
-
-Normalmente, quando você habilita `suspense`, é garantido que `data` estará sempre pronto na renderização:
-
-```jsx
-function Profile () {
- const { data } = useSWR('/api/user', fetcher, { suspense: true })
-
- // `data` nunca será `undefined`
- // ...
-}
-```
-
-No entanto, ao usá-lo junto com uma busca condicional ou busca dependente, `data` será `undefined` se a solicitação estiver **pausada**:
-
-```jsx
-function Profile () {
- const { data } = useSWR(isReady ? '/api/user' : null, fetcher, { suspense: true })
-
- // `data` será `undefined` se `isReady` é false
- // ...
-}
-```
-
-Se você quiser ler mais detalhes técnicos sobre essa restrição, cheque [a discussão aqui](https://github.com/vercel/swr/pull/357#issuecomment-627089889).
+import Callout from 'nextra-theme-docs/callout'
+
+# Suspense
+
+
+ React still doesn't recommend using `Suspense` in data frameworks like SWR (More information ). These APIs may change in the future as the results of our research.
+
+
+Você pode habilitar a opção `suspense` para usar SWR com React Suspense:
+
+```jsx
+import { Suspense } from 'react'
+import useSWR from 'swr'
+
+function Profile () {
+ const { data } = useSWR('/api/user', fetcher, { suspense: true })
+ return hello, {data.name}
+}
+
+function App () {
+ return (
+ loading...}>
+
+
+ )
+}
+```
+
+
+ Note que a opção `suspense` não é permitida a mudar no ciclo de vida.
+
+
+Em modo Suspense, `data` é sempre a resposta do fetch (então você não precisa checar se é `undefined`).
+Mas se um erro ocorreu, você precisa usar um [limite de erro (Error Boundary)](https://pt-br.reactjs.org/docs/error-boundaries.html) para o capturar:
+
+```jsx
+Could not fetch posts.}>
+ Loading posts...}>
+
+
+
+```
+
+
+Suspense mode suspends rendering until the data is ready, which means it causes waterfall problems easily. To avoid that, you should prefetch resources before rendering. [More information](/docs/prefetching)
+
+
+### Nota: Com Fetching Condicional
+
+Normalmente, quando você habilita `suspense`, é garantido que `data` estará sempre pronto na renderização:
+
+```jsx
+function Profile () {
+ const { data } = useSWR('/api/user', fetcher, { suspense: true })
+
+ // `data` nunca será `undefined`
+ // ...
+}
+```
+
+No entanto, ao usá-lo junto com uma busca condicional ou busca dependente, `data` será `undefined` se a solicitação estiver **pausada**:
+
+```jsx
+function Profile () {
+ const { data } = useSWR(isReady ? '/api/user' : null, fetcher, { suspense: true })
+
+ // `data` será `undefined` se `isReady` é false
+ // ...
+}
+```
+
+Se você quiser ler mais detalhes técnicos sobre essa restrição, cheque [a discussão aqui](https://github.com/vercel/swr/pull/357#issuecomment-627089889).
+
+### Server-Side Rendering
+
+When using suspense mode on the server-side (including pre-rendering in Next.js), it's **required** to provide the initial data via [fallbackData or fallback](/docs/with-nextjs#pre-rendering-with-default-data). This means that you can't use `Suspense` to fetch data on the server side, but either doing fully client-side data fetching, or fetch the data via the framework level data fetching method(such as getStaticProps in Next.js). More discussions can be found [here](https://github.com/vercel/swr/issues/1906).
diff --git a/pages/docs/suspense.ru.mdx b/pages/docs/suspense.ru.mdx
index 276ab956..71d3c2ac 100644
--- a/pages/docs/suspense.ru.mdx
+++ b/pages/docs/suspense.ru.mdx
@@ -3,12 +3,7 @@ import Callout from 'nextra-theme-docs/callout'
# Задержка (Suspense)
- Задержка (Suspense) в настоящее время является экспериментальной функцией React. Эти API могут значительно измениться без предупреждения, прежде чем станут частью React.
- Подробнее
-
-
-
- Обратите внимание, что React Suspense ещё не поддерживается в режиме SSR.
+ React still doesn't recommend using `Suspense` in data frameworks like SWR (More information ). These APIs may change in the future as the results of our research.
Вы можете включить опцию `suspense`, чтобы использовать SWR с React Suspense:
@@ -46,6 +41,10 @@ function App () {
```
+
+Suspense mode suspends rendering until the data is ready, which means it causes waterfall problems easily. To avoid that, you should prefetch resources before rendering. [More information](/docs/prefetching)
+
+
---
### Примечание: используя условную выборку
@@ -73,3 +72,7 @@ function Profile () {
```
Если вы хотите узнать больше технических подробностей об этом ограничении, смотрите [обсуждения здесь](https://github.com/vercel/swr/pull/357#issuecomment-627089889).
+
+### Server-Side Rendering
+
+When using suspense mode on the server-side (including pre-rendering in Next.js), it's **required** to provide the initial data via [fallbackData or fallback](/docs/with-nextjs#pre-rendering-with-default-data). This means that you can't use `Suspense` to fetch data on the server side, but either doing fully client-side data fetching, or fetch the data via the framework level data fetching method(such as getStaticProps in Next.js). More discussions can be found [here](https://github.com/vercel/swr/issues/1906).
diff --git a/pages/docs/suspense.zh-CN.mdx b/pages/docs/suspense.zh-CN.mdx
index b7a07a4b..1e5c91df 100644
--- a/pages/docs/suspense.zh-CN.mdx
+++ b/pages/docs/suspense.zh-CN.mdx
@@ -3,12 +3,7 @@ import Callout from 'nextra-theme-docs/callout'
# Suspense
- 目前 Suspense 是 React 的一个 实验 特性。这些 API 可能会有重大改变,并且在成为 React 一部分之前不会有任何警告。
- 了解更多
-
-
-
- 注意:SSR 模式还不支持 React Suspense。
+ React still doesn't recommend using `Suspense` in data frameworks like SWR (More information ). These APIs may change in the future as the results of our research.
你可以启用 `suspense` 选项,从而让 SWR 和 React Suspense 一起使用:
@@ -45,6 +40,10 @@ function App () {
```
+
+Suspense mode suspends rendering until the data is ready, which means it causes waterfall problems easily. To avoid that, you should prefetch resources before rendering. [More information](/docs/prefetching)
+
+
---
### 注意:使用条件请求
@@ -72,3 +71,7 @@ function Profile () {
```
如果你想阅读有关该限制的更多技术细节,请查看[这里的讨论](https://github.com/vercel/swr/pull/357#issuecomment-627089889)。
+
+### Server-Side Rendering
+
+When using suspense mode on the server-side (including pre-rendering in Next.js), it's **required** to provide the initial data via [fallbackData or fallback](/docs/with-nextjs#pre-rendering-with-default-data). This means that you can't use `Suspense` to fetch data on the server side, but either doing fully client-side data fetching, or fetch the data via the framework level data fetching method(such as getStaticProps in Next.js). More discussions can be found [here](https://github.com/vercel/swr/issues/1906).
diff --git a/pages/docs/typescript.en-US.mdx b/pages/docs/typescript.en-US.mdx
index 921d5a12..6298a3c9 100644
--- a/pages/docs/typescript.en-US.mdx
+++ b/pages/docs/typescript.en-US.mdx
@@ -18,8 +18,8 @@ useSWR({ a: '1', b: { c: '3', d: 2 } }, key => {})
useSWR(() => { a: '1', b: { c: '3', d: 2 } }, key => {})
// `arg0` will be inferred as string. `arg1` will be inferred as number
-useSWR(['user', 8], (arg0, arg1) => {})
-useSWR(() => ['user', 8], (arg0, arg1) => {})
+useSWR(['user', 8], ([arg0, arg1]) => {})
+useSWR(() => ['user', 8], ([arg0, arg1]) => {})
```
You can also explicitly specify the types for `key` and `fetcher`'s arguments.
diff --git a/pages/docs/typescript.es-ES.mdx b/pages/docs/typescript.es-ES.mdx
index 921d5a12..6298a3c9 100644
--- a/pages/docs/typescript.es-ES.mdx
+++ b/pages/docs/typescript.es-ES.mdx
@@ -18,8 +18,8 @@ useSWR({ a: '1', b: { c: '3', d: 2 } }, key => {})
useSWR(() => { a: '1', b: { c: '3', d: 2 } }, key => {})
// `arg0` will be inferred as string. `arg1` will be inferred as number
-useSWR(['user', 8], (arg0, arg1) => {})
-useSWR(() => ['user', 8], (arg0, arg1) => {})
+useSWR(['user', 8], ([arg0, arg1]) => {})
+useSWR(() => ['user', 8], ([arg0, arg1]) => {})
```
You can also explicitly specify the types for `key` and `fetcher`'s arguments.
diff --git a/pages/docs/typescript.ja.mdx b/pages/docs/typescript.ja.mdx
index c49afb61..f95b6047 100644
--- a/pages/docs/typescript.ja.mdx
+++ b/pages/docs/typescript.ja.mdx
@@ -18,8 +18,8 @@ useSWR({ a: '1', b: { c: '3', d: 2 } }, key => {})
useSWR(() => { a: '1', b: { c: '3', d: 2 } }, key => {})
// `arg0` は文字列として推測されます。 `arg1` は数値として推測されます。
-useSWR(['user', 8], (arg0, arg1) => {})
-useSWR(() => ['user', 8], (arg0, arg1) => {})
+useSWR(['user', 8], ([arg0, arg1]) => {})
+useSWR(() => ['user', 8], ([arg0, arg1]) => {})
```
`key` と `fetcher` の引数の型を明示的に指定することもできます。
diff --git a/pages/docs/typescript.ko.mdx b/pages/docs/typescript.ko.mdx
index 12a0f0b5..93681edb 100644
--- a/pages/docs/typescript.ko.mdx
+++ b/pages/docs/typescript.ko.mdx
@@ -17,9 +17,10 @@ useSWR(() => '/api/user', key => {})
useSWR({ a: '1', b: { c: '3', d: 2 } }, key => {})
useSWR(() => { a: '1', b: { c: '3', d: 2 } }, key => {})
+
// `arg0`은 stirng으로 추론됨. `arg1`은 number로 추론됨
-useSWR(['user', 8], (arg0, arg1) => {})
-useSWR(() => ['user', 8], (arg0, arg1) => {})
+useSWR(['user', 8], ([arg0, arg1]) => {})
+useSWR(() => ['user', 8], ([arg0, arg1]) => {})
```
명시적으로 `key`와 `fetcher`의 인자에 대한 타입을 지정할 수도 있습니다.
diff --git a/pages/docs/typescript.pt-BR.mdx b/pages/docs/typescript.pt-BR.mdx
index d70ab6fc..9144fdd2 100644
--- a/pages/docs/typescript.pt-BR.mdx
+++ b/pages/docs/typescript.pt-BR.mdx
@@ -1,91 +1,91 @@
-# TypeScript
-
-SWR é amigável para aplicações escritas em TypeScript, com tipos prontos para uso.
-
-## Uso Básico
-
-Por padrão, SWR irá inferir os tipos de argumento de `fetcher` a partir de `key`, então você pode ter os tipos preferidos automaticamente.
-
-### useSWR
-
-```typescript
-// `key` é inferido para ser `string`
-useSWR('/api/user', key => {})
-useSWR(() => '/api/user', key => {})
-
-// `key` será inferida como { a: string; b: { c: string; d: number } }
-useSWR({ a: '1', b: { c: '3', d: 2 } }, key => {})
-useSWR(() => { a: '1', b: { c: '3', d: 2 } }, key => {})
-
-// `arg0` será inferido como string. `arg1` será inferido como number
-useSWR(['user', 8], (arg0, arg1) => {})
-useSWR(() => ['user', 8], (arg0, arg1) => {})
-```
-
-Você pode também especificar os tipos para `key` e argumentos de `fetcher` explicitamente.
-
-```typescript
-import useSWR, { Key, Fetcher } from 'swr'
-
-const uid: Key = ''
-const fetcher: Fetcher = (id) => getUserById(id)
-
-const { data } = useSWR(uid, fetcher)
-// `data`será `User | undefined`.
-```
-
-### useSWRInfinite
-
-O mesmo para `swr/infinite`, você pode confiar na inferência automática de tipos ou especificar explicitamente os tipos por conta própria:
-
-```typescript
-import { SWRInfiniteKeyLoader } from 'swr/infinite'
-
-const getKey: SWRInfiniteKeyLoader = (index, previousPageData) => {
- // ...
-}
-
-const { data } = useSWRInfinite(getKey, fetcher)
-```
-
-## Genéricos
-
-Especificar o tipo de `data` é fácil. Por padrão, usará o tipo de retorno de `fetcher` (com `undefined` para o estado não pronto) como o tipo de `data`, mas você pode passá-lo como um parâmetro:
-
-```typescript
-// 🔹 A. Use um fetcher tipado:
-// `getUser` é (endpoint: string) => User.
-const { data } = useSWR('/api/user', getUser)
-
-// 🔹 B. Especifique o tipo de dados:
-// `fetcher` é geralmente retornado como `any`.
-const { data } = useSWR('/api/user', fetcher)
-```
-
-Se você quer adicionar tipos para outras opções de SWR, você também pode importar esses tipos diretamente:
-
-```typescript
-import useSWR from 'swr'
-import type { SWRConfiguration } from 'swr'
-
-const config: SWRConfiguration = {
- fallbackData: "fallback",
- revalidateOnMount: false
- // ...
-}
-
-const { data } = useSWR('/api/data', fetcher, config)
-```
-
-## Tipos de Middleware
-
-Existem alguns definições de tipo extras que você pode importar para ajudar a adicionar tipos ao seu middleware personalizado.
-
-```typescript
-import useSWR, { Middleware, SWRHook } from 'swr'
-
-const swrMiddleware: Middleware = (useSWRNext: SWRHook) => (key, fetcher, config) => {
- // ...
- return useSWRNext(key, fetcher, config)
-}
-```
+# TypeScript
+
+SWR é amigável para aplicações escritas em TypeScript, com tipos prontos para uso.
+
+## Uso Básico
+
+Por padrão, SWR irá inferir os tipos de argumento de `fetcher` a partir de `key`, então você pode ter os tipos preferidos automaticamente.
+
+### useSWR
+
+```typescript
+// `key` é inferido para ser `string`
+useSWR('/api/user', key => {})
+useSWR(() => '/api/user', key => {})
+
+// `key` será inferida como { a: string; b: { c: string; d: number } }
+useSWR({ a: '1', b: { c: '3', d: 2 } }, key => {})
+useSWR(() => { a: '1', b: { c: '3', d: 2 } }, key => {})
+
+// `arg0` será inferido como string. `arg1` será inferido como number
+useSWR(['user', 8], ([arg0, arg1]) => {})
+useSWR(() => ['user', 8], ([arg0, arg1]) => {})
+```
+
+Você pode também especificar os tipos para `key` e argumentos de `fetcher` explicitamente.
+
+```typescript
+import useSWR, { Key, Fetcher } from 'swr'
+
+const uid: Key = ''
+const fetcher: Fetcher = (id) => getUserById(id)
+
+const { data } = useSWR(uid, fetcher)
+// `data`será `User | undefined`.
+```
+
+### useSWRInfinite
+
+O mesmo para `swr/infinite`, você pode confiar na inferência automática de tipos ou especificar explicitamente os tipos por conta própria:
+
+```typescript
+import { SWRInfiniteKeyLoader } from 'swr/infinite'
+
+const getKey: SWRInfiniteKeyLoader = (index, previousPageData) => {
+ // ...
+}
+
+const { data } = useSWRInfinite(getKey, fetcher)
+```
+
+## Genéricos
+
+Especificar o tipo de `data` é fácil. Por padrão, usará o tipo de retorno de `fetcher` (com `undefined` para o estado não pronto) como o tipo de `data`, mas você pode passá-lo como um parâmetro:
+
+```typescript
+// 🔹 A. Use um fetcher tipado:
+// `getUser` é (endpoint: string) => User.
+const { data } = useSWR('/api/user', getUser)
+
+// 🔹 B. Especifique o tipo de dados:
+// `fetcher` é geralmente retornado como `any`.
+const { data } = useSWR('/api/user', fetcher)
+```
+
+Se você quer adicionar tipos para outras opções de SWR, você também pode importar esses tipos diretamente:
+
+```typescript
+import useSWR from 'swr'
+import type { SWRConfiguration } from 'swr'
+
+const config: SWRConfiguration = {
+ fallbackData: "fallback",
+ revalidateOnMount: false
+ // ...
+}
+
+const { data } = useSWR('/api/data', fetcher, config)
+```
+
+## Tipos de Middleware
+
+Existem alguns definições de tipo extras que você pode importar para ajudar a adicionar tipos ao seu middleware personalizado.
+
+```typescript
+import useSWR, { Middleware, SWRHook } from 'swr'
+
+const swrMiddleware: Middleware = (useSWRNext: SWRHook) => (key, fetcher, config) => {
+ // ...
+ return useSWRNext(key, fetcher, config)
+}
+```
diff --git a/pages/docs/typescript.ru.mdx b/pages/docs/typescript.ru.mdx
index 4ab5fd19..3406bd18 100644
--- a/pages/docs/typescript.ru.mdx
+++ b/pages/docs/typescript.ru.mdx
@@ -18,8 +18,8 @@ useSWR({ a: '1', b: { c: '3', d: 2 } }, key => {})
useSWR(() => { a: '1', b: { c: '3', d: 2 } }, key => {})
// `arg0` подразумевается как строка. `arg1` подразумевается как число
-useSWR(['user', 8], (arg0, arg1) => {})
-useSWR(() => ['user', 8], (arg0, arg1) => {})
+useSWR(['user', 8], ([arg0, arg1]) => {})
+useSWR(() => ['user', 8], ([arg0, arg1]) => {})
```
You can also explicitly specify the types for `key` and `fetcher`'s arguments.
diff --git a/pages/docs/typescript.zh-CN.mdx b/pages/docs/typescript.zh-CN.mdx
index 346f96a5..1e1160a4 100644
--- a/pages/docs/typescript.zh-CN.mdx
+++ b/pages/docs/typescript.zh-CN.mdx
@@ -18,8 +18,8 @@ useSWR({ a: '1', b: { c: '3', d: 2 } }, key => {})
useSWR(() => { a: '1', b: { c: '3', d: 2 } }, key => {})
// `arg0` 将被推断为 string. `arg1` 将被推断为 number
-useSWR(['user', 8], (arg0, arg1) => {})
-useSWR(() => ['user', 8], (arg0, arg1) => {})
+useSWR(['user', 8], ([arg0, arg1]) => {})
+useSWR(() => ['user', 8], ([arg0, arg1]) => {})
```
你还可以显式地指定 `key` 和 `fetcher` 参数的类型。
diff --git a/pages/index.en-US.mdx b/pages/index.en-US.mdx
index dc32d591..71efd5c0 100644
--- a/pages/index.en-US.mdx
+++ b/pages/index.en-US.mdx
@@ -31,10 +31,10 @@ SWR is a strategy to first return the data from cache (stale), then send the fet
import useSWR from 'swr'
function Profile() {
- const { data, error } = useSWR('/api/user', fetcher)
+ const { data, error, isLoading } = useSWR('/api/user', fetcher)
if (error) return failed to load
- if (!data) return loading...
+ if (isLoading) return loading...
return hello {data.name}!
}
```
@@ -78,7 +78,7 @@ And a lot [more](/docs/getting-started).
-SWR is created by the same team behind [Next.js](https://nextjs.org), the React framework.
+SWR is created by the same team behind [Next.js](https://nextjs.org), the React framework.
Follow [@vercel](https://twitter.com/vercel) on Twitter for future project updates.
Feel free to join the [discussions on GitHub](https://github.com/vercel/swr/discussions)!
diff --git a/pages/index.es-ES.mdx b/pages/index.es-ES.mdx
index b7c67e88..1e1f5a78 100644
--- a/pages/index.es-ES.mdx
+++ b/pages/index.es-ES.mdx
@@ -33,10 +33,10 @@ SWR es una estrategia para devolver primero los datos en caché (obsoletos), lue
import useSWR from 'swr'
function Profile() {
- const { data, error } = useSWR('/api/user', fetcher)
+ const { data, error, isLoading } = useSWR('/api/user', fetcher)
if (error) return failed to load
- if (!data) return loading...
+ if (isLoading) return loading...
return hello {data.name}!
}
```
diff --git a/pages/index.ja.mdx b/pages/index.ja.mdx
index 0010b17d..19e9d5e4 100644
--- a/pages/index.ja.mdx
+++ b/pages/index.ja.mdx
@@ -33,10 +33,10 @@ SWR は、まずキャッシュからデータを返し(stale)、次にフ
import useSWR from 'swr'
function Profile() {
- const { data, error } = useSWR('/api/user', fetcher)
+ const { data, error, isLoading } = useSWR('/api/user', fetcher)
if (error) return failed to load
- if (!data) return loading...
+ if (isLoading) return loading...
return hello {data.name}!
}
```
diff --git a/pages/index.ko.mdx b/pages/index.ko.mdx
index bb4dbda6..0307bdc0 100644
--- a/pages/index.ko.mdx
+++ b/pages/index.ko.mdx
@@ -31,10 +31,10 @@ SWR은 먼저 캐시(스태일)로부터 데이터를 반환한 후, fetch 요
import useSWR from 'swr'
function Profile() {
- const { data, error } = useSWR('/api/user', fetcher)
+ const { data, error, isLoading } = useSWR('/api/user', fetcher)
if (error) return failed to load
- if (!data) return loading...
+ if (isLoading) return loading...
return hello {data.name}!
}
```
@@ -78,7 +78,7 @@ SWR은 더 나은 경험을 구축할 수 있도록 속도, 정확성, 안정성
-SWR은 React 프레임워크인 [Next.js](https://nextjs.org)를 만든 동일한 팀이 만들었습니다.
+SWR은 React 프레임워크인 [Next.js](https://nextjs.org)를 만든 동일한 팀이 만들었습니다.
앞으로의 프로젝트 업데이트를 위해 Twitter에서 [@vercel](https://twitter.com/vercel)을 팔로우해주세요.
언제라도 편하게 [GitHub discussions](https://github.com/vercel/swr/discussions)에 참여해주세요!
diff --git a/pages/index.pt-BR.mdx b/pages/index.pt-BR.mdx
index 7f7022ea..70552440 100644
--- a/pages/index.pt-BR.mdx
+++ b/pages/index.pt-BR.mdx
@@ -1,83 +1,83 @@
----
-title: React Hooks para Data Fetching
----
-
-import Callout from 'nextra-theme-docs/callout'
-import Features from 'components/features'
-import Bleed from 'nextra-theme-docs/bleed'
-
-{
-// wrapped with {} to mark it as javascript so mdx will not put it under a p tag
-}
-{SWR }
-
-
-
-O nome "SWR" é derivado de `stale-while-revalidate`, uma técnica de invalidação de cache HTTP popularizada pela [HTTP RFC 5861](https://tools.ietf.org/html/rfc5861).
-SWR é a estratégia de primeiro retornar os dados do cache (stale), depois enviar a solicitação de fetch (revalidate), e finalmente retornar com os dados atualizados.
-
-
- Com SWR, componentes irão receber um fluxo de dados constante e automático .
- E a UI sempre será rápida e reativa .
-
-
-
- [Comece a Usar](/docs/getting-started) · [Exemplos](/examples/basic) · [Blog](/blog/swr-v1) · [Repositório do GitHub](https://github.com/vercel/swr)
-
-
-## Visão geral
-
-```jsx
-import useSWR from 'swr'
-
-function Profile() {
- const { data, error } = useSWR('/api/user', fetcher)
-
- if (error) return falhou em carregar
- if (!data) return carregando...
- return Olá {data.name}!
-}
-```
-
-Nesse exemplo, o hook `useSWR` aceita uma string `key` e uma função `fetcher`. `key` é um identificador único do dado (normalmente a URL da API)
-e será passado para `fetcher`. `fetcher` pode ser qualquer função assíncrona que retorna o dado, você pode usar o fetch nativo ou ferramentas como Axios.
-
-O hook retorna 2 valores: `data` e `error`, baseado no status da solicitação.
-
-## Funcionalidades
-
-Com apenas uma linha de código, você pode simplificar a lógica de carregamento de dados em seu projeto, e também ter todas essas maravilhas prontas pra uso:
-
-- Obtenção de dados **rápida**, **leve** e **reutilizável**
-- **Cache** integrado e desduplicação de requisições
-- Experiência em **tempo-real**
-- Transporte e protocolo agnóstico
-- Suporte à SSR / ISR / SSG
-- Pronto para uso com TypeScript
-- React Native
-
-SWR abrange todos os aspectos de velocidade, correção e estabilidade para ajudá-lo a criar experiências melhores:
-
-- Navegação de páginas rápida
-- Pesquisa em intervalo
-- Dependência de dados
-- Revalidação ao focar
-- Revalidação ao recuperar conexão
-- Mutação local (UI otimista)
-- Retentativa inteligente
-- Recuperação de paginação e posição de scroll
-- React Suspense
-
-E muito [mais](/docs/getting-started).
-
-## Comunidade
-
-
-
-
-
-
-
-SWR é criado pelo mesmo time por trás do [Next.js](https://nextjs.org), o framework React. Siga [@vercel](https://twitter.com/vercel) no Twitter para atualizações futuras do projeto.
-
-Sinta-se livre para juntar-se as [discussões no GitHub](https://github.com/vercel/swr/discussions)!
+---
+title: React Hooks para Data Fetching
+---
+
+import Callout from 'nextra-theme-docs/callout'
+import Features from 'components/features'
+import Bleed from 'nextra-theme-docs/bleed'
+
+{
+// wrapped with {} to mark it as javascript so mdx will not put it under a p tag
+}
+{SWR }
+
+
+
+O nome "SWR" é derivado de `stale-while-revalidate`, uma técnica de invalidação de cache HTTP popularizada pela [HTTP RFC 5861](https://tools.ietf.org/html/rfc5861).
+SWR é a estratégia de primeiro retornar os dados do cache (stale), depois enviar a solicitação de fetch (revalidate), e finalmente retornar com os dados atualizados.
+
+
+ Com SWR, componentes irão receber um fluxo de dados constante e automático .
+ E a UI sempre será rápida e reativa .
+
+
+
+ [Comece a Usar](/docs/getting-started) · [Exemplos](/examples/basic) · [Blog](/blog/swr-v1) · [Repositório do GitHub](https://github.com/vercel/swr)
+
+
+## Visão geral
+
+```jsx
+import useSWR from 'swr'
+
+function Profile() {
+ const { data, error, isLoading } = useSWR('/api/user', fetcher)
+
+ if (error) return falhou em carregar
+ if (isLoading) return carregando...
+ return Olá {data.name}!
+}
+```
+
+Nesse exemplo, o hook `useSWR` aceita uma string `key` e uma função `fetcher`. `key` é um identificador único do dado (normalmente a URL da API)
+e será passado para `fetcher`. `fetcher` pode ser qualquer função assíncrona que retorna o dado, você pode usar o fetch nativo ou ferramentas como Axios.
+
+O hook retorna 2 valores: `data` e `error`, baseado no status da solicitação.
+
+## Funcionalidades
+
+Com apenas uma linha de código, você pode simplificar a lógica de carregamento de dados em seu projeto, e também ter todas essas maravilhas prontas pra uso:
+
+- Obtenção de dados **rápida**, **leve** e **reutilizável**
+- **Cache** integrado e desduplicação de requisições
+- Experiência em **tempo-real**
+- Transporte e protocolo agnóstico
+- Suporte à SSR / ISR / SSG
+- Pronto para uso com TypeScript
+- React Native
+
+SWR abrange todos os aspectos de velocidade, correção e estabilidade para ajudá-lo a criar experiências melhores:
+
+- Navegação de páginas rápida
+- Pesquisa em intervalo
+- Dependência de dados
+- Revalidação ao focar
+- Revalidação ao recuperar conexão
+- Mutação local (UI otimista)
+- Retentativa inteligente
+- Recuperação de paginação e posição de scroll
+- React Suspense
+
+E muito [mais](/docs/getting-started).
+
+## Comunidade
+
+
+
+
+
+
+
+SWR é criado pelo mesmo time por trás do [Next.js](https://nextjs.org), o framework React. Siga [@vercel](https://twitter.com/vercel) no Twitter para atualizações futuras do projeto.
+
+Sinta-se livre para juntar-se as [discussões no GitHub](https://github.com/vercel/swr/discussions)!
diff --git a/pages/index.ru.mdx b/pages/index.ru.mdx
index 70e3c069..b49010b8 100644
--- a/pages/index.ru.mdx
+++ b/pages/index.ru.mdx
@@ -32,10 +32,10 @@ import Bleed from 'nextra-theme-docs/bleed'
import useSWR from 'swr'
function Profile() {
- const { data, error } = useSWR('/api/user', fetcher)
+ const { data, error, isLoading } = useSWR('/api/user', fetcher)
if (error) return ошибка загрузки
- if (!data) return загрузка...
+ if (isLoading) return загрузка...
return привет, {data.name}!
}
```
@@ -79,7 +79,7 @@ SWR охватывает все аспекты скорости, правиль
-SWR создана той же командой, что стоит за React фреймворком [Next.js](https://nextjs.org).
+SWR создана той же командой, что стоит за React фреймворком [Next.js](https://nextjs.org).
Следите за [@vercel](https://twitter.com/vercel) в Твиттере, чтобы получать новости о проекте.
Присоединяйтесь к [обсуждениям на GitHub](https://github.com/vercel/swr/discussions)!
diff --git a/pages/index.zh-CN.mdx b/pages/index.zh-CN.mdx
index 96e9bad1..125ba5b3 100644
--- a/pages/index.zh-CN.mdx
+++ b/pages/index.zh-CN.mdx
@@ -32,10 +32,10 @@ import Bleed from 'nextra-theme-docs/bleed'
import useSWR from 'swr'
function Profile() {
- const { data, error } = useSWR('/api/user', fetcher)
+ const { data, error, isLoading } = useSWR('/api/user', fetcher)
if (error) return failed to load
- if (!data) return loading...
+ if (isLoading) return loading...
return hello {data.name}!
}
```
@@ -90,7 +90,7 @@ SWR 涵盖了性能,正确性和稳定性的各个方面,以帮你建立更
/>
-SWR 由 [Next.js](https://nextjs.org)(React 框架)背后的同一团队创建。
+SWR 由 [Next.js](https://nextjs.org)(React 框架)背后的同一团队创建。
在 Twitter 上关注 [@vercel](https://twitter.com/vercel) 未来项目更新
欢迎加入 [GitHub 上的讨论](https://github.com/vercel/swr/discussions)!
diff --git a/public/img/devtools/cache-view.jpg b/public/img/devtools/cache-view.jpg
new file mode 100644
index 00000000..738ea04b
Binary files /dev/null and b/public/img/devtools/cache-view.jpg differ
diff --git a/public/img/understanding/fallback.svg b/public/img/understanding/fallback.svg
new file mode 100644
index 00000000..a13aaabf
--- /dev/null
+++ b/public/img/understanding/fallback.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+ fallback isValidating isLoading fetching value fetching isValidating new value Fallback Revalidate data:
\ No newline at end of file
diff --git a/public/img/understanding/fetch-and-revalidate.svg b/public/img/understanding/fetch-and-revalidate.svg
new file mode 100644
index 00000000..75107487
--- /dev/null
+++ b/public/img/understanding/fetch-and-revalidate.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+ undefined isValidating isLoading fetching value fetching isValidating new value Fetch and Revalidate Revalidate data:
\ No newline at end of file
diff --git a/public/img/understanding/isloading.gif b/public/img/understanding/isloading.gif
new file mode 100644
index 00000000..3fc3c893
Binary files /dev/null and b/public/img/understanding/isloading.gif differ
diff --git a/public/img/understanding/key-change-fallback.svg b/public/img/understanding/key-change-fallback.svg
new file mode 100644
index 00000000..c2ed681b
--- /dev/null
+++ b/public/img/understanding/key-change-fallback.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+ fallback(1) isValidating isLoading fetching value(1) isValidating Key Change + Fallback Key Change fallback(2) isValidating isLoading fetching value(2) Revalidate new value(2) data:
\ No newline at end of file
diff --git a/public/img/understanding/key-change-previous-data-fallback.svg b/public/img/understanding/key-change-previous-data-fallback.svg
new file mode 100644
index 00000000..d7d176cb
--- /dev/null
+++ b/public/img/understanding/key-change-previous-data-fallback.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+ fallback(1) isValidating isLoading fetching value(1) isValidating Key Change + Previous Data + Fallback Key Change value(1) isValidating isLoading fetching value(2) Revalidate new value(2) data:
\ No newline at end of file
diff --git a/public/img/understanding/key-change-previous-data.svg b/public/img/understanding/key-change-previous-data.svg
new file mode 100644
index 00000000..a8120635
--- /dev/null
+++ b/public/img/understanding/key-change-previous-data.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+ undefined isValidating isLoading fetching value(1) isValidating Key Change + Previous Data Key Change value(1) isValidating isLoading fetching value(2) Revalidate new value(2) data:
\ No newline at end of file
diff --git a/public/img/understanding/key-change.svg b/public/img/understanding/key-change.svg
new file mode 100644
index 00000000..73dbacd0
--- /dev/null
+++ b/public/img/understanding/key-change.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+ undefined isValidating isLoading fetching value(1) isValidating Key Change Key Change undefined isValidating isLoading fetching value(2) Revalidate new value(2) data:
\ No newline at end of file
diff --git a/public/video/optimistic-ui.mp4 b/public/video/optimistic-ui.mp4
new file mode 100644
index 00000000..d62ed646
Binary files /dev/null and b/public/video/optimistic-ui.mp4 differ