usesWR로 제작된 Form 화면의 메모
useSWR
)시각적 안정성 및 캐시
먼저 SWR과 React Query를 활용하는 동기를 소개한다.SWR이 고유 키 캐시에 연결되지 않은 경우 loading fallback이 표시됩니다.fallback은 한순간에도'낫시 앤 시'로 나타나기 때문에 사용자에게'원활하지 않은 인상'을 줄 수 있다고 밝혔다.그러나 SWR과 React Query는 캐시가 있을 때 SKIP fallback을 사용하기 때문에 사용자의 조작을 방해하지 않고 시각적 안정성을 제공할 수 있다.이 장점은 반복적인 화면 이동에서 현저하게 나타난다.
import useSWR from "swr";
function Profile() {
const { data, error } = useSWR("/api/user", fetcher);
if (error) return <div>failed to load</div>;
if (!data) return <div>loading...</div>; // 有キャッシュ時はSKIP、画面がチカっとしない
return <div>hello {data.name}!</div>; // 新キャッシュを得た時、差し替わる
}
시각적 감지 성능을 향상시키기 위해'예측 가능한 UI를 즉시 얼마나 반환해야 하는가'라는 의식은 없어서는 안 된다.SWR과 React Query의 캐시 메커니즘이 이를 위해 기여했으며, 자연스럽게 비동기 처리가 렌더링 블록을 일으키지 않는 메커니즘을 탈 수 있었다.이 은혜를 어떻게 의식하는가가 이 프로그램 라이브러리들을 처리하는 요점이다.말단 구성 요소 포켓staledvalue
Form+SWR에서 특히 주의해야 할 것은 편집 화면입니다.SWR에 설치된 화면의 일련의 처리를 살펴보겠습니다.
props.defaultValue
에서 받은 값은 명확하게 고쳐야 한다.이 처리로 useEffect를 복원하는 방법이 있습니다.일반적인 제어 구성 요소를 예로 들다.function Template(props: { defaultValue: string }) {
// defaultValue として保持
const [value, setValue] = React.useState(props.defaultValue);
const handleChange = React.useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value)
},
[setValue]
);
// defaultValue のリセット
React.useEffect(() => {
setValue(props.defaultValue);
}, [setValue, props.defaultValue]);
return <input type="text" value={value} onChange={handleChange} />;
}
export function Page() {
const { data, error } = useSWR("/api/user/edit", () =>
axios.get<string>("/api/user/edit").then(({ data }) => data);
);
if (error) return <div>failed to load</div>;
return <Template defaultValue={data || ""} />;
}
위에서 설명한 대로 SWR을 사용하는 "Edit 화면"에서는 초기 값을 업데이트하는 메커니즘을 처음부터 고려해야 합니다.비제어 어셈블리의 경우
이전의 예에서 제어 구성 요소가 표시한 것이지만react-hook-form 등에서 나는 비제어 구성 요소를 사용하여
defaultValue
의 실현을 주었다고 생각한다.useEffect
에는 setValue를 사용하는 방법이 있지만 더 효과적인 방법이 있다.집행
defaultValues
시 부여useForm
useEffect
의 deps 배열에 추가useEffect
와 reset
함수를 이용하여 복원export function Page() {
const { data, error } = useSWR("/api/user/edit", () =>
axios.get<{ name: string }>("/api/user/edit").then(({ data }) => data)
);
const { register, reset } = useForm({
defaultValues: { name: "" }, // [1]
});
React.useEffect(() => {
reset({ name: data.name }); // [3]
}, [data]); // [2]
if (error) return <div>failed to load</div>;
return <input type="text" {...register("name")} />;
}
react-hook-form의 장점은 제어 구성 요소에 비해 설명량이 적다는 것이다.이렇게 usesWR과 useForm을 함께 사용하면 끝에서 조작하지 않는 것이 가장 좋다.(컨트롤 어셈블리도 마찬가지)loading fallback을 쉽게 사용하지 마세요.
이전 샘플에서
<div>loading...</div>
의loading fallback을 생략하려고 했습니다.'예측 가능한 UI를 즉시 반환해야 한다'는 의식으로 처리하면 input 요소만 즉시 표시되고, 데이터를 확보한 뒤 다시 쓰면 시각적 안정성을 제공한다.(열거된 input 요소만 예측할 수 있는 상황)loading fallback을 쉽게 표시할 수 있지만 여기에는 아직 시간이 있다.레이아웃을 예측할 수 있다면 블록이 나타나는 것을 최대한 피해야 한다.주제 밖의 말은 어떤 데이터를 얻든 판면 디자인을 할 수 있어 향후 전단 수요가 늘어날 것으로 보인다.
입력 거부에 isValidating 사용
이번 경우usesWR에서 되돌아오는 isValidate의 input 요소 입력 거부에 사용할 수 있습니다.개발 과정에서 API 회복 속도가 빨라 눈치채지 못했을 수도 있지만 실제 API는 상황에 따라 몇 초가 걸릴 수도 있다.따라서 validate에서도 입력을 받아들였다면'인풋 요소에 절반을 입력했는데 갑자기 조상으로 돌아왔다'는 문제가 발생할 수 있다.이것은 사용자가 원하는 것이 아니기 때문에 isValidating은 input 요소의disabled에 사용하기에 적합하지 않습니까?
export function Page() {
const { data, error, isValidating } = useSWR("/api/user/edit", () =>
axios.get<string>("/api/user/edit").then(({ data }) => data)
);
const { register, reset } = useForm({
defaultValues: { name: "" },
});
React.useEffect(() => {
reset({ name: data });
}, [data]);
if (error) return <div>failed to load</div>;
return (
<input
type="text"
disabled={isValidating} // <- ここ
{...register("name")}
/>
);
}
비동기 처리가 사용자의 조작을 차단했지만 시각적 안정성을 확보했다.이 실시는 균형이기 때문에 이해관계자 간에 의제를 제기할 수 있다.기능을 활용하다
mutate를 활용하면 시각적 안정성이 더 좋습니다.위에서 말한 바와 같이staledvalue를revalidatedvalue로 바꾸는 순간이 반드시 발생할 것이다. 이것은 사용자의 눈에는 다소 조화롭지 못할 것이다.
구성 요소가 편집과 업데이트 대상의 캐시에 문제가 없으면,mutate를 사용합니다.세 번째 파라미터에 허위 정보를 제공하면revalidate 없이 캐시를 바꿀 수 있기 때문에 API 호출 횟수도 최소한으로 조절할 수 있습니다.(이하 공식 문서 참조)
import useSWR, { useSWRConfig } from "swr";
function Profile() {
const { mutate } = useSWRConfig();
const { data } = useSWR("/api/user", fetcher);
return (
<div>
<h1>My name is {data.name}.</h1>
<button
onClick={async () => {
const newName = data.name.toUpperCase();
// 再検証をせずに直ちにローカルデータを更新します
mutate("/api/user", { ...data, name: newName }, false);
// ソースを更新するために API にリクエストを送信します
await requestUpdateUsername(newName);
// ローカルデータが最新であることを確かめるために再検証(再取得)を起動します
mutate("/api/user");
}}
>
Uppercase my name!
</button>
</div>
);
}
단, 자원 캐시 범위가 넓으면 주의해야 합니다.위에서 말한 바와 같이 사용자 상세 화면에서 사용자 이름을 변경한 경우 사용자 상세 화면만 업데이트하는 캐시가 부족할 수 있습니다.모든mutate를 실행해야 하는 상황에서mutate 누출이 발생하거나 나중에 추가가 필요하면 업데이트 처리가 복잡해집니다.따라서Form화면에서 본 원고에서 언급한staledvalue의 복원은 일차 처리로 비교보험을 실시한다.
어느 시기에 fetch가 어디에서mutate를 집행하는지는 디자인에 따라 다르다.mutate에서revalidate도 화를 내는 상황에서 API 호출을 한 번 실행했기 때문에revalidate IfStale가 사실인 경우(기본값) 어두운 mutate revalidate를 피하려고 합니다.
초기값 리셋을 보장하는 테스트 용례
MSW 등 모크 서버로 개발하면 캐시 문제에 주의하기 어려울 것이다.모듈 응답은 기본적으로 같은 반응을 답장하기 때문에 실제 API와 결합할 때 문제를 주목하는 것도 적지 않다.
따라서 납품 초기 값 상태의 구성 요소에 대해서는'revalidated value로 바뀌었습니다'라는 테스트 용례를 써야 합니다.MSW의 경우
server.use
함수는handler의intercept를 사용할 수 있습니다.이러한 테스트 용례를 써서 고속 캐시가 있을 때revalidatedvalue로 바꾸는 행위를 재현할 수 있습니다.test("revalidated value に書き換えられること", async () => {
// 無キャッシュ時レンダリング、設定済みの MSW handler レスポンスが表示されることを確認
const renderResult = render(<Template />);
await waitFor(() =>
expect(renderResult.getByRole("textbox")).toHaveDisplayValue("TARO")
);
// ここまでで、有キャッシュ状態になる
// 設定済みの MSW handler のレスポンスを書き換える
server.use(
rest.get("/api/user/edit", (_, res, ctx) => {
return res(ctx.json({ name: "JIRO" }));
})
);
// 有キャッシュで再レンダリング、書き換えた MSW handler のレスポンスが表示されることを確認
renderResult.rerender(<Template />);
await waitFor(() =>
expect(renderResult.getByRole("textbox")).toHaveDisplayValue("JIRO")
);
renderResult.unmount();
});
총결산
SWR은 API의 정규화, 비정규화, UX 최적화, 구성 요소 구성 등 다방면에 관련된다.실장자는 미세한 순간에 생기는 불협화감에 민감해야 하는 등 깊이 파고들면 난이도가 높다.언뜻 보기에는 간단하지만 다양한 기술을 발휘할 수 있는 심오한 도서관이라고 생각한다.
Reference
이 문제에 관하여(usesWR로 제작된 Form 화면의 메모), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/takepepe/articles/form-with-useswr텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)