remix.run

Sending data to a server using remix run


I am having issues sending data to a server using remix run - I'm not sure I fully understand how useAction data works. I understand how the useLoaderData functions work but when you are trying to send data to a server I get errors.

What I want to do is send a post request to my server when I click a button - if I try and call create cart in the handleCLick event it says that createCart is not a function when it is

const submit = useSubmit()

 function action({ request }) {
  is this where i do my POST api call?

}

async function handleClick(event) {
    await createCart(id, amount)
  }

Cant seem to find any documentation which tells you how to do this?


Solution

  • With Remix, actions always run on the server. It is the method that Remix will call when you POST to a route.

    // route.tsx
    
    import { json, type ActionArgs, type LoaderArgs } from '@remix-run/node'
    import { Form, useActionData, useLoaderData, useSubmit } from '@remix-run/react'
    import { createCart } from '~/models/cart.server' // your app code
    import { getUserId } from '~/models/user.server' 
    
    // loader is called on GET
    export const loader = async ({request}: LoaderArgs) => {
      // get current user id
      const id = await getUserId(request)
      // return
      return json({ id })
    }
    
    // action is called on POST
    export const action = async ({request}: ActionArgs) => {
      // get the form data from the POST
      const formData = await request.formData()
      // get the values from form data converting types
      const id = Number(formData.get('id'))
      const amount = Number(formData.get('amount'))
      // call function on back end to create cart
      const cart = await createCart(id, amount)
      // return the cart to the client
      return json({ cart })
    }
    
    // this is your UI component
    export default function Cart() {
      // useLoaderData is simply returning the data from loader, it has already
      // been fetched before component is rendered. It does NOT do the actual 
      // fetch, Remix fetches for you
      const { id } = useLoaderData<typeof loader>()
      // useActionData returns result from action (it's undefined until
      // action has been called so guard against that for destructuring
      const { cart } = useActionData<typeof action>() ?? {}
      // Remix handles Form submit automatically so you don't really
      // need the useSubmit hook
      const submit = useSubmit()
      const handleSubmit = (e) => {
        submit(e.target.form)
      }
      return (
        <Form method="post">
          {/* hidden form field to pass back user id *}
          <input type="hidden" name="id"/>
          <input type="number" name="amount"/>
          {/* Remix will automatically call submit when you click button *}
          <button>Create Cart</button>
          {/* show returned cart data from action */}
          <pre>{JSON.stringify(cart, null, 2)}</pre>
        </Form>
      )
    }