Giter Club home page Giter Club logo

remix-component-data's Introduction

Remix Component Data

Screenshot of Component Data with error handling

This is a proof of concept for showing how you can expose loader and actions functions from your components to use in your routes.

Once the API has settled down, I'll create a NPM package.

View example site at https://remix-component-data.herokuapp.com/

๐Ÿ›  Usage

Your route module still controls the current page's loader. To add component loaders, you will need to import them manually into your route.

// route.tsx
import Widget1, { loader as widget1loader } from '~/components/widget1'
import Widget2, { loader as widget2loader } from '~/components/widget2'

// widget1.tsx
export let loader: LoaderFunction = async () => {
  return json({ message: 'Hello from Widget 1' })
}

You then need to call these component loaders and return the data with your existing route loader.

// route.tsx
import { callComponentLoaders } from '~/utils/remix-component-data'

export let loader: LoaderFunction = async () => {
  // your route's data
  let routeData = { ... }
  // call all component loaders
  const componentData = await callComponentLoaders({
    widget1: widget1Loader,
    widget2: widget2Loader,
  })
  return json({ routeData, componentData })
}

You can now access the component data in your default route export. Wrap your component inside <ComponentData> and pass the specific component's data with the data prop.

// route.tsx
import ComponentData from '~/components/ComponentData'

export default function Index() {
  let { routeData, componentData } = useLoaderData()
  return (
    <div className="widgetContainer">
      {/* Wrap widget in <ComopnentData> and pass in widget loader data */}
      <ComponentData loaderData={componentData.widget1}>
        <Widget1 />
      </ComponentData>
      <ComponentData loaderData={componentData.widget2}>
        <Widget2 />
      </ComponentData>
    </div>
  )
}

๐Ÿ—‚ Accessing Component Loader Data

You can access the loader data for the component using the useComponentData hook. This does not provide access to the route's loader data. You can pass that in manually using props or with the useMatches hook.

// widget1.tsx
function Widget1() {
  const { message } = useComponentData()
}

๐Ÿ‚ Using Component Actions

You can now export actions from your components. As with loaders, the enclosing route is still responsible for dispatching the action to the component. Once an action has been successfully handled, Remix will call the route's loaders as it usually does.

// route.tsx
export let action: ActionFunction = async args => {
  // call the component actions
  const componentResponse = await callComponentActions(args, {
    // the action id must be unique <ComponentData id={uniqe}>
    // you can add params and this will be matched
    // post?postId=1 will match post?postId
    ['post?postId']: componentAction,
  })
  // check to see if the action was handled
  if (componentResponse.handled) {
    return componentResponse.response
  }
  // if not, process as route action
  return null
}

// wrap component in <ComponentData> and pass in props: id and actionData
export default function () {
  const { routeData, componentData } = useComponentData()
  const actionData = useActionData()
  return (
    <>
      {routeData.postIds
        .map(postId => `post?postId=${postId}`)
        .map(postId => (
          <ComponentData
            id={postId}
            key={postId}
            loaderData={componentData[postId]}
            actionData={actionData?.[postId]}
          >
            <Widget4 />
          </ComponentData>
        ))}
    </>
  )
}
// widget.tsx
// componentParams added to match data from <ComponentData id={key}>
export let action: ActionFunction = async ({ request, componentParams }) => {
  const session = await getSession(request.headers.get('Cookie'))
  // get param value from pattern `post?postId`
  const { postId } = componentParams
  const key = `liked-${postId}`
  const isLiked = session.get(key)
  if (isLiked) {
    session.unset(key)
  } else {
    session.set(key, true)
  }

  return json(
    { message: `You liked Post ${postId}`, isLiked },
    { headers: { 'Set-Cookie': await commitSession(session) } },
  )
}

function Widget4() {
  let { postId, isLiked } = useComponentData()
  const actionData = useComponentActionData()
  if (actionData) {
    isLiked = actionData.isLiked
  }

  return (
    <div className="widget">
      <h1>Post {postId}</h1>
      <div>
        <Form method="post">
          {/* <ComponetData.Params> adds hidden input with unique key */}
          <ComponentData.Params />
          <button type="submit">{isLiked ? 'Unlike' : 'Like'}</button>
        </Form>
      </div>
    </div>
  )
}

๐Ÿ’ฃ Handling errors

Your component can also have a custom CatchBoundary and ErrorBoundary. Simply add it as a property to your default export. Just like a route, you can access the data using the useComponentCatch hook and the error prop.

// widget1.tsx
function Widget1() {}
export default Widget1

Widget1.CatchBoundary = function () {
  const caught = useComponentCatch()
  ...
}
Widget1.ErrorBoundary = function({error}) {
  return <div>{error.message}</div>
}

๐Ÿ™ Feedback welcome

Please provide feedback either via Issues or on the Remix Discord. Thanks for taking a look.

remix-component-data's People

Contributors

kiliman avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.