#okay to use a Resource to update a store?

8 messages · Page 1 of 1 (latest)

exotic compass
#

My use-case is to load a user data, display it and when I hit a button to update the use in the database, fetch the new data but keep the data visible while fetching.

Usually, using Resource shows either "loading" or the resolved output.

<Resource
  value={reposResource}
  onPending={() => (data.loading = true)}
  onResolved={(repos) => {
    data.loading = false;
    data.value = repos;
    return null;
  }}
/>
autumn sandal
#

Have you tried it an it doesn't work?

exotic compass
#

It works. I was just wondering if I was doing something wrong

buoyant roost
#

It feels bad to update a store inside render. You can use reposResource.loading instead I think. The intention with <Resource> is that you don't need to save data.value because repos can just be used to render directly. If I saw more of your code I could help try to find a more idomatic way to set this up.

mild tide
#

@buoyant roost sorry for the question on an existing thread but it looks like you might have answered a question for a problem that I'm trying to figure out when using Resource.

I have a "List" component that is "just" a wrapper where I'm using Resourceto request some data and that data is passed as props to a child component.

I have used the code example from the docs [https://qwik.builder.io/tutorial/reactivity/resource/]

I tried 2 approaches (both work)
1st - I pass everything as a single to the child component
2nd - I pass each prop separately

The problem is that in both cases there are TS type problems

<Resource
  value={worksResource}
  onPending={() => <>Loading...</>}
  onRejected={(error) => <>Error: {error.message}</>}
  onResolved={(works) => (
    <section class="list">
      {works.map((work) => (
        // 1st approach
        // <Item key={index} work={work} />
        // 2nd approach
        <Item
          key={work.id}
          id={work.id}
          client={work.client}
          project={work.project}
          slug={work.slug}
          image={work.image}
        />
      ))}
    </section>
  )}
/>

On 1st approach

Type 'string' is not assignable to type 'IWork'

The IWork is the interface I have on the child component

interface IWork {
  id: string | number;
  client: string;
  project: string;
  slug: string;
  image: string;
}

On 2nd approach, it complains about each one of the props

Property 'id' does not exist on type 'string'
Property 'client' does not exist on type 'string'
Property 'project' does not exist on type 'string'
Property 'image' does not exist on type 'string'

What am I doing wrong?
Is this because of the serialization as it handles the props as strings?

Thanks

buoyant roost
#

Is it possible Item is not typed properly. Both these approaches are working for me:

import { Resource, component$, useResource$ } from "@builder.io/qwik";

interface IWork {
  id: string | number;
  client: string;
  project: string;
  slug: string;
  image: string;
}

const Approach1Item = (props: { work: IWork }) => {
  return (
    <pre>
      <code>{JSON.stringify(props.work)}</code>
    </pre>
  );
};

const Approach2Item = (props: IWork) => {
  return (
    <pre>
      <code>{JSON.stringify(props)}</code>
    </pre>
  );
};

export default component$(() => {
  const worksResource = useResource$(async () => {
    const res: IWork = {
      id: "",
      client: "",
      project: "",
      slug: "",
      image: "",
    };

    return [res];
  });

  return (
    <Resource
      value={worksResource}
      onPending={() => <>Loading...</>}
      onRejected={(error) => <>Error: {error.message}</>}
      onResolved={(works) => (
        <>
          <section class="list">
            {works.map((work) => (
              <Approach1Item key={work.id} work={work} />
            ))}
          </section>
          <section class="list">
            {works.map((work) => (
              <Approach2Item
                key={work.id}
                id={work.id}
                client={work.client}
                project={work.project}
                slug={work.slug}
                image={work.image}
              />
            ))}
          </section>
        </>
      )}
    />
  );
});
mild tide
#

Thanks for your reply
yeah, for me also works fine on dev mode, but not able to run the build as it complains about the type.
Meanwhile tried another implementation that works and I don't have TS complaining about nothing.

But meanwhile, I'm facing another challenge. I have a detail page where before printing out the data I need to manipulate the data.
From what I see using Resource to render the data we cannot do that, so tried to do it in a "more traditional" way, on useResource$ I do a fetch to the endpoint, and on the .then() I do the manipulation that I need to do and return the new structured data. The problem's on the return as it always returns me a promise and not the new data

#
import { component$, useResource$ } from "@builder.io/qwik";
import { useLocation } from "@builder.io/qwik-city";
import "./index.scss";

interface IWorks {
  id: string | number;
  client: string;
  project: string;
  slug: string;
  images: string;
  allImages?: string[];
}

export const fetchWork = (slug: String) => {
  return fetch(`https://apiEndpoint?slug=${slug}`);
};

export default component$(() => {
  const loc = useLocation();

  const work = useResource$(() => {
    const res = fetchWork(loc.params.slug)
      .then((res) => {
        const data = res.json() as Promise<IWorks[]>;

        return data.then(
          (res) => {
            const workItem = res[0];
            const allImages = workItem.images.split(",");
            workItem.allImages = allImages;

            return res[0];
          },
          (error) => {
            console.log(error);
          }
        );
      })
      .catch((err) => {
        console.log(err);
        throw new Error("Error fetching works");
      });

    return res;
  });

  return (
    <>
      <div class="work">
        {work.images.length > 1 && (
            <ul class="work__slideshow">
              <li class="work__nav">« prev</li>
              <li class="work__nav">next »</li>
            </ul>
          )}
        <div class="work__item">
          <div class="work__img-wrapper">
            {work.images.map((image, i) => (
                <div
                  key={i}
                  class="work__img"
                  style={{
                    backgroundImage: `url(https://imageUrl/${image})`,
                  }}
                ></div>
              ))}
          </div>
          <div class="work__content-wrapper">
            <div class="work__content">
              <h2 class="work__title">{work.client}</h2>
              <p class="work__info">{work.description}</p>
            </div>
          </div>
        </div>
      </div>
    </>
  );
});