Replies: 22 comments 30 replies
-
Just hit this myself for the exact same use case -- I have a modal, the user gets errors on submit and hits cancel. When they re-open the modal the I want |
Beta Was this translation helpful? Give feedback.
-
Running into a similar issue with stale data in the render function causing this wonkiness: I don't understand. What's even useful about previous data when I'm calling I tried wrapping Has anyone figured out a clean solution that doesn't require managing data in multiple places? (and hey @WesleyKapow 👋 … small world!) |
Beta Was this translation helpful? Give feedback.
-
Yeah...would love this. Generally people have suggested workarounds like moving the data fetching further down the component tree but that tends to either break container/display-component patterns or make them unwieldy like |
Beta Was this translation helpful? Give feedback.
-
The nice thing about using the fetcher is being able to directly link a simple component's UI to the https://stackblitz.com/edit/node-9ta3ig?file=app/routes/index.tsx Here's a simplified version of a form I made today that brought this up for me. It's a 2 phase contact signup + verification form, where state from the previous submit is round-tripped in order to activate (and be submitted with) the 2nd phase of the form. This could be done with 2 forms/fetchers instead, and it might be more clear that way, but even in that case, if the visibility of the 2nd form/phase is dependent on the |
Beta Was this translation helpful? Give feedback.
-
is there any workaround to reset fetcher ? |
Beta Was this translation helpful? Give feedback.
-
I investigated what it would take to implement this and it needs to be done within react-router-dom since remix creates a wrapper around that. I have added a feature request here remix-run/react-router#10231. I was able to work around this by mounting and unmounting the component based on what I was using for a visible state. I don't think this is optimal but it gets the job done. I was inspired by the route-modal example which may also solve this issue, at least for now. |
Beta Was this translation helpful? Give feedback.
-
Another workaround I've used is to not use the value in const [myData, setMyData] = useState();
useEffect(() => {
if (fetcher.state === "submitting" || fetcher.state === "loading") {
setMyData(undefined);
} else if (fetcher.state === "idle") {
setMyData(fetcher.data?.myData);
}
}, [fetcher.state, fetcher.data]);
// Use `myData` instead of `fetcher.data.myData` Kind of ugly but it works if you don't have too much data to worry about and don't want to create a reset route for whatever reason. |
Beta Was this translation helpful? Give feedback.
-
You could also reload the page to reset the action data (and clean it from previous validation error fields)... const navigate = useNavigate();
const location = useLocation();
...
<button
className="ml-auto"
onClick={() => {
setIsEditing(false);
// -> to reset potential action validation errors
navigate(location.pathname);
}}
>
Annuler
</button> |
Beta Was this translation helpful? Give feedback.
-
Here's a working fetcher with export type FetcherWithComponentsReset<T> = FetcherWithComponents<T> & {
reset: () => void;
};
export function useFetcherWithReset<T>(): FetcherWithComponentsReset<T> {
const fetcher = useFetcher<T>();
const [data, setData] = useState(fetcher.data);
useEffect(() => {
if (fetcher.state === "idle") {
setData(fetcher.data);
}
}, [fetcher.state, fetcher.data]);
return {
...fetcher,
data: data as T,
reset: () => setData(undefined),
};
} ⚡️ StackBlitz https://stackblitz.com/edit/remix-run-remix-vhgfqt?file=app%2Froutes%2F_index.tsx |
Beta Was this translation helpful? Give feedback.
-
Not much related, but seems usefull to share... Instead of reseting it, I trust in the last "loading" state and wrap the fetcher in a global Provider, all around my application, then fetch through a hook. The loading state of the fetcher has a last "handshake" step, where it asks for the data chunk while in loading. So I never trust in iddle fetchers, but in the last step of the handshake. (loading + data chunk) The state transition on the fetcher works so it gets the data from the fetcher in "loading" state still, and considers it done after that ( iddle ). So you get a mirrored result on fetcher.data. One for loading with data and the first one for iddle with data. Whenever you have a data chunk in "loading" state, means that it is the last handshake of loading state. |
Beta Was this translation helpful? Give feedback.
-
I think with the new |
Beta Was this translation helpful? Give feedback.
-
Having the same problem while implementing a toast message: 1 - Delete an item Maybe using a different fetcher key would solve the problem but I need the key to be the same so I can show some loading state in a component separated from the route and the toast itself |
Beta Was this translation helpful? Give feedback.
-
btw why doesn't the fetcher resets its data when the route change? |
Beta Was this translation helpful? Give feedback.
-
Modified @kiliman's solution with a fetcher key. https://gist.github.com/arunmmanoharan/38d313f28dc17637a0e3cfa8c6205bd5 |
Beta Was this translation helpful? Give feedback.
-
You can also use a useState value for the useFetcher key. Resetting this value to a new one also resets the fetcher, since it listens to the actual state of the value. See my example code: function ExampleComponent() {
const [key, setKey] = useState("a123");
const fetcher = useFetcher({ key });
const [showModal, setShowModal] = useState(false);
const [dataChanged, setDataChanged] = useState(false);
function handleShowModal() {
setShowModal(true);
}
function handleCloseModal() {
setShowModal(false);
}
useEffect(() => {
if (fetcher.state === "idle" && fetcher.data?.ok && !!fetcher.data?.msg && !dataChanged && showModal) {
setDataChanged(true);
}
}, [dataChanged, showModal, fetcher]);
useEffect(() => {
if (dataChanged && !showModal) {
setKey("b321");
}
}, [dataChanged, showModal]);
useEffect(() => {
setDataChanged(false);
}, [key]);
return (
<div>
<button type="button" onClick={handleShowModal}>Show Modal</button>
<Modal show={showModal} onClose={handleCloseModal}>
<>
{dataChanged ? (
<div>Data has been changed</div>
) : (
null
)}
<fetcher.Form method="post">
<input type="email" name="email" />
{!dataChanged ? (
<button type="submit">Change E-Mail</button>
): (
<button type="button" size="small" onClick={handleCloseModal}>Close Modal</button>
)}
</fetcher.Form>
</>
</Modal>
</div>
);
} |
Beta Was this translation helpful? Give feedback.
-
What is the actual overhead of using random keys and with that creating new fetchers all over the place? Say, I have a message board where people use an editor to create or edit posts. Also let's say, the editor uses useFetcher({ key: 'edit-' + Date().toISOString()}) which over the lifetime of a session could create dozens if not hundreds of fetchers in that client. Is this even noticeable? Greets |
Beta Was this translation helpful? Give feedback.
-
The Link component you can provide a reloadDocument prop which will reset the state of fetcher. fetcher.data will be undefined in this case. Hope this helps anyone running into this issue. |
Beta Was this translation helpful? Give feedback.
-
I thought I needed a way to reset the fetcher, but adding more entropy to the key fixed it. A param did the job in this case, just go to town with that key. Hope this helps anyone. const params = useParams()
const fetcher = useTypedFetcher<typeof fragmentLoader>({
key: `filter-${cat}-${params.urlId}`,
}) |
Beta Was this translation helpful? Give feedback.
-
The reset via key trick works but having a native |
Beta Was this translation helpful? Give feedback.
-
Just hit a wall where I want a modal to close when the fetcher data submits successfully but not when it errors, and as the state persists I can’t open it ever again 😆 @ryanflorence pls |
Beta Was this translation helpful? Give feedback.
-
I think there’s a baseline concept here that’s getting missed. The idea
with a form fetcher is to link an html form to some remote endpoint. The
thing getting missed is that the native form has a reset(), but the fetcher
doesn’t. It’s a missing feature. Someone asserted here that the point is
just to update local state and that it shouldn’t be used directly. If so
then this requires extra useState or useEffect magic which Ryan has claimed
is his goal to avoid. It seems like such a small and obvious thing I’m
actually quite surprised there’s so much debate about it.
T
…On Mon, Oct 14, 2024 at 8:22 AM ✦ freddie ***@***.***> wrote:
Just hit a wall where I want a modal to close when the fetcher data
submits successfully but not when it errors, and as the state persists I
can’t open it ever again 😆
@ryanflorence <https://github.com/ryanflorence> pls
—
Reply to this email directly, view it on GitHub
<#2749 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAEXVZV2FALZQSXIGNIGRB3Z3POU3AVCNFSM5WZ73QGKU5DIOJSWCZC7NNSXTOSENFZWG5LTONUW63SDN5WW2ZLOOQ5TCMBZGM4DGNZY>
.
You are receiving this because you commented.Message ID:
***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
I tried the method described here, but it didn't work in my environment. I'd like to avoid using “any”, but... Operation has been confirmed in Remix spa mode. Why did I want to do this? However, after the Snackbar was displayed, when the same operation was repeated, the Snackbar could not be displayed because fetcher.data was not empty.
|
Beta Was this translation helpful? Give feedback.
-
What is the new or updated feature that you are suggesting?
Reset the fetcher data returned from the action? Using something Like
fetcher.reset()
Why should this feature be included?
Example Flow would be
fetcher?.data?.formError
from the actionthe user clicks on dismiss error
or something similar, I want to reset fetcher data returned from action.Yeah there can be a workaround like
fetcher.type === "done" && fetcher.data.error
then set a client-side state withfetcher?.data?.formError
and use this state instead offetcher?.data?.formError
in UI. Now we have full control over the error message.But doing this manual work in every component is a little annoying.
https://www.loom.com/share/f20262a4773d460c9f4d962cc8880431
In the video,
Here If I could do like
fetcher.reset()
on closing the modal, that would be awesome. [Though I have no idea how hard is it to implement or even if it possible to implement this 😅]Let me know if something awesome exists that solves this problem without needing to maintain a state on the client side. 🙇
Beta Was this translation helpful? Give feedback.
All reactions