0

Many articles writing about how to return pending promise and work with React suspense but it's not working in real world. They don't consider if the component got visited second time, and it won't refetch the data from the server.

e.g. => https://dev.to/darkmavis1980/a-practical-example-of-suspense-in-react-18-3lln?signin=true

The below example would only work for the first time we visit the component but not re-fetch data for the following times.

Any idea to let it work to prevent not doing re-fetching?

Component


const dataFetchWithWrapPromise = (url) => {
    return wrapPromise(window.fetch(url, {
    }));
}

const resource = dataFetchWithWrapPromise('http://localhost:3000/data');

function Articles() {
    const data = resource.read();

    React.useEffect(() => {
        return () => {
            resource.reset();
        }
    }, []);
    return (
        <>
            <h1>Data</h1>
            <pre>
                {JSON.stringify(data, null, 4)}
            </pre>
        </>
    );
}

export default Articles;

function wrapPromise(promise) {
    let status = 'pending';
    let response;

    const suspender = promise.then(
        async res => {
            status = 'success';
            response = await res.json();
        },
        err => {
            status = 'error';
            response = err;
        },
    );

    const handler = {
        pending: () => {
            throw suspender;
        },
        error: () => {
            throw response;
        },
        success: () => {
            console.log(response)
            return response
        },
        default: () => {
            throw suspender;
        },
    };

    const read = () => {
        const result = handler[status] ? handler[status]() : handler.default();
        return result;
    };
    const reset = () => {
        if(status!=='pending') {
            status = 'pending';
            response = undefined;
        }

    }
    return { read, reset };
}

export default wrapPromise;
newBike
  • 14,385
  • 29
  • 109
  • 192
  • Hello. Has the data been changed in any way, so it needs to be re-fetched? If yes, can you identify a mechanism where the component can find out about this need? – José Ramírez Nov 01 '22 at 01:14
  • The data fetch depends on the parameters change from the parent. So, why cache? Is there anyway to get rid of this cache. And, React Suspense is really complicating and breaking the pattern. make ugly logic in this case :( – newBike Nov 01 '22 at 01:22

1 Answers1

0

Ok, so I think I got you covered. It so happens that I liked <Suspense> ever since I heard of it. I stumbled with it in my learning of asynchronous JavaScript because I was coding wj-config. This preface is just to let you know that I'm no React master, but it so happens that I ended up creating a React example for wj-config v2.0.0, which is currently in BETA 2. This example does what you want.

So no more chit-chat. The code of interest is here.

It is a table component that loads person data from Mockaroo. The web page (parent) has two controls to specify the number of rows wanted as well as the minimum birth date wanted. Whenever the value of any of those controls change, the person data is re-fetched. The table itself uses <Suspense> in two places.

The component module starts by defining the fetching functions needed for person and country data. Then it declares some variables that are captured in scopes later on. The starting promise is required for the first render. Its resolver is exposed through startingResolver, and the starting promise is wrapped as per the <Suspense> mechanics that you clearly know.

Focus your attention now to the PersonsTable function. It sets up a useEffect call to re-trigger the data fetching operations based on changes of props. As I'm not a super master in ReactJS, maybe there's a better way than props. I just know props will trigger the effect automatically, so I used them.

On the first render, the starting promise is thrown, but it will never be fulfilled since it is a bogus promise. The code inside useEffect makes this promise resolve at the same time the fetching promise resolves. Then, using the fetching promise, the readPersons function is defined.

NOTE: I'm not a native English speaker. Pardon my horrible "persons" instead of "people" mistake. :-( I'll correct whenever I have time.

Anyway, with this set up, you'll have completed your goal. The linked code sample goes beyond this by having an inner <Suspense> that waits for country data, but I suppose an explanation is not needed since I believe the question is now covered.

Hope this helps!

José Ramírez
  • 872
  • 5
  • 7