在 useState Hook 中使用 SWR 数据

Jat*_*ani 5 javascript reactjs swr

  const fetcher = (url: string) => fetch(url).then((r) => r.json());

  const { data, error } = useSWR(
    "https://some.com/api",
    fetcher,
  );
Run Code Online (Sandbox Code Playgroud)

有没有办法像这样在 useState 挂钩中添加数据

  const fetcher = (url: string) => fetch(url).then((r) => r.json());

  const { data, error } = useSWR(
    "https://meme-api.herokuapp.com/gimme/5",
    fetcher,
  );

const [memes,setMemes]=useState(data);
Run Code Online (Sandbox Code Playgroud)

因为我想在某个时刻连接数据以进行无限滚动

New*_*bie 5

将数据从一个变量传输到另一个变量的最快解决方案是使用钩子useEffect。当data发生变化时,更新memes.

useEffect(() => { setMemes(data); }, [data])
Run Code Online (Sandbox Code Playgroud)

无限滚动

更好的解决方案是使用 SWR 提供的无限滚动解决方案。您可以在此处记录不同的选项。

普通获取

这种情况下,你也可以考虑直接使用 fetch 函数,直接将数据追加到 memes 列表中:

const [ memes, setMemes ] = useState([]);

async function fetchAnotherPage() {
    const data = (await fetch('https://meme-api.herokuapp.com/gimme/5')).json();
    setMemes(value => [...value, ...data.memes]);
}

useEffect(() => fetchAnotherPage(), []);
Run Code Online (Sandbox Code Playgroud)


T.J*_*der 2

由于https://meme-api.herokuapp.com/gimme/5每次调用总是返回新数据,因此useSWR不太适合这种情况,而且,它从缓存中检索并将其提供给您的代码,然后重新验证并(可能)调用您的代码进行更新,而不告诉您是否这是第一个结果或更新,使得很难执行您所描述的操作。

相反,我只是fetch直接使用而不是尝试做 SWR 的事情;看评论:

// Start with no memes
const [memes,setMemes] = useState([]);

// Use a ref to track an `AbortController` so we can:
// A) avoid overlapping fetches, and
// B) abort the current `fetch` operation (if any) on unmount
const fetchControllerRef = useRef(null);

// A function to fetch memes
const fetchMoreMemes = () => {
    if (!fetchControllerRef.current) {
        fetchControllerRef.current = new AbortController();
        fetch("https://meme-api.herokuapp.com/gimme/5", {signal: fetchControllerRef.current.signal})
        .then(response => {
            if (!response.ok) {
                throw new Error(`HTTP error ${response.status}`);
            }
            return response.json();
        })
        .then(newMemes => {
            setMemes(memes => memes.concat(newMemes.memes));
        })
        .catch(error => {
            // ...handle/report error...
        })
        .finally(() => {
            fetchControllerRef.current = null;
        });
    }
};

// Fetch the first batch of memes
useEffect(() => {
    fetchMoreMemes();
    return () => {
        // Cancel the current `fetch` (if any) when the component is unmounted
        fetchControllerRef.current?.abort();
    };
}, []);
Run Code Online (Sandbox Code Playgroud)

当您想获取更多模因时,请致电fetchMoreMemes

实例:

const {useState, useEffect, useRef} = React;

const Example = () => {
    // Start with no memes
    const [memes,setMemes] = useState([]);

    // Use a ref to track an `AbortController` so we can:
    // A) avoid overlapping fetches, and
    // B) abort the current `fetch` operation (if any) on unmount
    const fetchControllerRef = useRef(null);

    // A function to fetch memes
    const fetchMoreMemes = () => {
        if (!fetchControllerRef.current) {
            fetchControllerRef.current = new AbortController();
            fetch("https://meme-api.herokuapp.com/gimme/5", {signal: fetchControllerRef.current.signal})
            .then(response => {
                if (!response.ok) {
                    throw new Error(`HTTP error ${response.status}`);
                }
                return response.json();
            })
            .then(newMemes => {
                // I'm filtering out NSFW ones here on SO
                setMemes(memes => memes.concat(newMemes.memes.filter(({nsfw}) => !nsfw)));
            })
            .catch(error => {
                // ...handle/report error...
            })
            .finally(() => {
                fetchControllerRef.current = null;
            });
        }
    };

    // Fetch the first batch of memes
    useEffect(() => {
        fetchMoreMemes();
        return () => {
            // Cancel the current `fetch` (if any) when the component is unmounted
            fetchControllerRef.current && fetchControllerRef.current.abort();
        };
    }, []);
    
    const message = memes.length === 1 ? "1 meme:" : `${memes.length} memes:`;
    return <div>
        <div>{message} <input type="button" value="More" onClick={fetchMoreMemes}/></div>
        <ul>
            {/* `index` as key is ONLY valid because our array only grows */}
            {memes.map(({postLink}, index) => <li key={index}>{postLink}</li>)}
        </ul>
    </div>
};


ReactDOM.render(<Example />, document.getElementById("root"));
Run Code Online (Sandbox Code Playgroud)
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
Run Code Online (Sandbox Code Playgroud)