1

I am a newbie using Solid JS (and JS altogether for that matter) and I'm facing a seemingly strange behaviour when trying to populate a (custom) <Table> component using data from an API using fetch().

This is my <Table>:

import { Component, For } from "solid-js";
import "jquery";
import "datatables.net-bs5";

const Table: Component<{ id: string; columns: string[]; data: any[] }> = ({
  id,
  columns,
  data,
}) => {
  return (
    <>
      <div class="table-responsive">
        <table id={id} class="table table-striped table-hovr">
          <thead>
            <For each={columns}>{(col: string) => <th>{col}</th>}</For>
          </thead>
          <tbody>
            <For each={data}>
              {(row: {}) => (
                <tr>
                  <For each={Object.values(row)}>
                    {(d: any) => <td>{d}</td>}
                  </For>
                </tr>
              )}
            </For>
          </tbody>
        </table>
      </div>

    </>
  );
};

export default Table;

this is where I want to put the data:

import { Component, createSignal} from 'solid-js';
import Table from '../components/Table';
import "jquery";
import "datatables.net-bs5";
import { getRequest } from '../utils/utils';
import Customer from '../entities/Customer';



const Customers: Component = () => {
    const [customers, setCustomers] = createSignal<Customer[]>([]);

    const columns = [
        'ID Cliente',
        'Numero Cliente',
        'Azienda?', 
        'Nome',
        'Cognome',
        'Sesso',
        'Data di nascita',
        'Via',
        'Città',
        'CAP',
        'Telefono',
        'E-mail'
       ] /* Display name of the columns */

       
   const req =  getRequest("/customers").then((data) => setCustomers(data)
   ).catch((e) => window.location.href="/");

   

    return <>
            <h2>Clienti</h2>
            <br />
            <Table id="main-table" columns={columns} data={customers()}></Table>
            </>
    
}

export default Customers;

function createStore<T>(arg0: never[]): [any, any] {
    throw new Error('Function not implemented.');
}

and this is the function I use to fetch the data:

export async function getRequest(endpoint : string) {
  const response = await fetch(config.api_url + endpoint, {
        method: "GET",
        "headers": {
            "Authorization": config.jwt
        }
    });
    return response.json();
}

Basically the problem is that I see the data inside the table on the webpage only the first time after I change something in the <Table> and save. As I reload the page (and for all other accessess) I only see the <th>s and an empty table body.

I think I'm misunderstanding the SolidJS Signal, but I'm not sure.

Could you please give me an hint on what am I doing wrong?

Thanks. NC

I tried using the Solid JS Signal without success

snnsnn
  • 10,486
  • 4
  • 39
  • 44

1 Answers1

1

You can either use a resource or use the fetch API directly. A resource allows you to fetch the data before rendering the component.

Using a resource

Solid has a dedicated API for fetching and rendering remote data which is created using createResource:

https://www.solidjs.com/docs/latest/api#createresource

Resources allows you to manage query parameters and renders different element in different stages of an ongoing request.

const [data, { mutate, refetch }] = createResource(getQuery, fetchData);

Here, getQuery is a signal carrying query parameters. It is optinoal and updating it causes automatic refetch.

fetchData is the fetcher function that returns a promise in form of a resource.

data is a getter function with additional properties:

  • data() returns the success value.
  • data.states returns the state of the ongoing promise.
  • data.error returns the reason for failure in case the promise is rejected. I am going to keep it short in order to avoid repeating the documentation.

Here is a working demo: https://playground.solidjs.com/anonymous/6b063de1-584c-4691-aa38-cf8791070962

import { render } from "solid-js/web";
import { createSignal, Switch, Match, createResource } from "solid-js";

const getUsers = () => new Promise(resolve => {
  const users = [
    'Vivian Li',
    'Lina Delgado',
    'Aimee Navarro',
    'Enzo Larsen',
    'Diana Doyle',
    'Elspeth Meyers',
    'Michael Roberson',
    'Junior Sparks',
    'Daniela Orozco',
    'Esme Larson',
  ];
  setTimeout(() => resolve(users), 1000);
});

function App() {
  const [data] = createResource(getUsers);

  return (
    <div>
      <Switch fallback={<div>Not Found</div>}>
        <Match when={data.state === 'pending' || data.state === 'unresolved'}>
          Loading...
        </Match>
        <Match when={data.state === 'ready'}>
          {JSON.stringify(data())}
        </Match>
        <Match when={data.state === 'errored'}>
          {JSON.stringify(data.error)}
        </Match>
      </Switch>
    </div>
  );
}

render(App, document.getElementById("app")!);

Using the fetch API directly

You can use fetch API directly in your component but you have to manage the state yourself in a way that reflects the status of the ongoing request.

To make the code self sufficient, I am going to use a promise but you can use fetch function and code works the same.

Here you can find a demo: https://playground.solidjs.com/anonymous/a0b41605-f490-4787-bba0-130312cc2ba9

import { render } from "solid-js/web";
import { createSignal, Switch, Match } from "solid-js";

const getUsers = () => Promise.resolve([
  'Vivian Li',
  'Lina Delgado',
  'Aimee Navarro',
  'Enzo Larsen',
  'Diana Doyle',
  'Elspeth Meyers',
  'Michael Roberson',
  'Junior Sparks',
  'Daniela Orozco',
  'Esme Larson',
]);

function App() {

  interface State {
    status: 'pending' | 'resolved' | 'rejected';
    data?: any;
    error?: any;
  };

  const [state, setState] = createSignal<State>({ status: 'pending' });

  const handleClick = () => {
    getUsers()
      .then(data => setState({ status: 'resolved', data, error: undefined }))
      .catch(error => setState({ status: 'rejected', error }));
  }

  return (
    <div>
      <div>
      <button type="button" onClick={handleClick}>
        Fetch Users
      </button>
      </div>
      <Switch fallback={<div>Not Found</div>}>
        <Match when={state().status === 'pending'}>
          Loading...
        </Match>
        <Match when={state().status === 'resolved'}>
          {JSON.stringify(state().data)}
        </Match>
        <Match when={state().status === 'rejected'}>
          {JSON.stringify(state().error)}
        </Match>
      </Switch>
    </div>
  );
}

render(App, document.getElementById("app")!);

In the demo, data is fetch by a click handler, but you can move the fetching logic into the component body and do the fetching automatically.

function App() {
  interface State { status: 'pending' | 'resolved' | 'rejected', data?: any, error?: any };
  const [state, setState] = createSignal<State>({ status: 'pending' });
  
  getUsers()
      .then(data => setState({ status: 'resolved', data, error: undefined }))
      .catch(error => setState({ status: 'rejected', error }));

  return (
    <div>
      <div>
        <button type="button">
          Fetch Users
        </button>
      </div>
      <Switch fallback={<div>Not Found</div>}>
        <Match when={state().status === 'pending'}>
          Loading...
        </Match>
        <Match when={state().status === 'resolved'}>
          {JSON.stringify(state().data)}
        </Match>
        <Match when={state().status === 'rejected'}>
          {JSON.stringify(state().error)}
        </Match>
      </Switch>
    </div>
  );
}
snnsnn
  • 10,486
  • 4
  • 39
  • 44