Search code examples
javascriptremix.run

How to pass data from ActionFunction to Loader function in Remix.run?


I'm making an application with Remix.run. It consists of multiple steps where user fills forms and in the end gets success screen. All form steps are located on single route, but success is on it's own route.

How everything works

I have redux-like (actually XState, but it is not important) message processing on server. It receives current (or initial for initial load) state, message type and data from fields. It then returns new state to client and page is rendered based on that state. Server don't have any storage an I don't want to introduce one, since it will complicate things.

// form page
export async function loader() {
  return json(await getInitialState())
}

export async function action({request}) {
  let fd = await request.formData();
  let {current_state, message_type, ...data} = Object.fromEntries(fd.entries());
  return json(await getNextState(current_state, {type: message_type, data}));
}

export function unstable_shouldReload({submission}) {
  return !submission
}

export default FormPage() {
  let loaderData = useLoaderData();
  let actionData = useActionData();
  // awful, isn't it?
  let currentState = actionData || loaderData;
}

My problems

First my problem was that loader was triggered on every form submit. So, ActionFunction returns new state, but LoaderFunction returns initial state. It would be fine, but to get initial state, loader runs API, so it's wasted calls. I solved it with unstable_ShouldReload, and it's kind of ok, but not really.

Now I got to final step of the form and feel that I have same problem, but now I have separate route, so I can't just prevent running LoaderFunction. I need LoaderFunction to actually run on new page, and get data passed from previous steps for it. Basically, it's the same problem - I need to use data posted in form usable inside loader.

I tried to replace code above using session cookie with all the data, but I have other problems with it. Action returns cookie with session data, loader reads it and returns new state. Unfortunately it fails in few scenarios. If there is current session, user can reload page, prevent sending info again and get result as data was sent. It is not how web "usually" works. I can change it, by destroying session on each load, but then very first POST request yields no result, if there is no JS on user side. Otherwise it is best solution yet (but limited by size of the cookie).

Question

How to pass data from last action to next loader, or process POST request in loader itself?


Solution

  • How to pass data from last action to next loader

    The only way is using sessions and or cookies. If you want to only be able to read the session data once and ensure it's not there anymore after a reload you could use session.flash(key, value), then in your loader, when you do session.get(key) it will remove the flash key from the session, if you commit the session in the loader then the cookie will be updated to don't have that key anymore, after a reload it will not be there.

    process POST request in loader itself

    This is not possible, loader functions only handle GET and OPTIONS requests, for every other request method only the action function is used.