Search code examples
reactjsreact-hooksreact-typescriptreact-tsx

React tsx - How to pass an array to node


I am using axios to get an array from a JSON file and I want to use the same array over multiple nodes / pages.

Currently I'm using this on each page var printers = GetPrinters();

I was trying call GetPrinters() from the App.tsx and pass it to each page via a router but I couldn't seem to get that to work. Is there a way I can store the array on state and access it on any node?

I plan to make 4 more pages similar to MonoPrints.tsx that all need access to the data from the array, they won't be updating the array at all, just accessing it.

I would like to use the best practice for this kind of thing and am stuck with repeating the call for each page at the moment.

Site Structure: App.tsx

import { BrowserRouter, Routes, Route } from "react-router-dom";
import { TonerLevels } from "./pages/TonerLevels";
import { MonoPrints } from "./pages/MonoPrints";
import { MonoCopies } from "./pages/MonoCopies";
import { ColourPrints } from "./pages/ColourPrints";
import { ColourCopies } from "./pages/ColourCopies";

export function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/TonerLevels" element={<TonerLevels />}></Route>
        <Route path="/MonoPrints" element={<MonoPrints />}></Route>
        <Route path="/MonoCopies" element={<MonoCopies />}></Route>
        <Route path="/ColourPrints" element={<ColourPrints />}></Route>
        <Route path="/ColourCopies" element={<ColourCopies />}></Route>
      </Routes>
    </BrowserRouter>
  );
}

Pages/ TonerLevels

export function TonerLevels() {
  var printers = GetPrinters();
  return (
    <div>
      <Nav></Nav>
      <table className="table table-dark table-hover">
        <thead>
          <tr>
            <th>Location</th>
            <th>IP</th>
            <th>Name</th>
            <th>Serial</th>
            <th>Black %</th>
            <th>Yellow %</th>
            <th>Magenta %</th>
            <th>Cyan %</th>
            <th>K1 %</th>
            <th>K2 %</th>
            <th>Current Monthly Prints</th>
            <th>Current Monthly Copies</th>
          </tr>
        </thead>
        <tbody className="table-group divider">
          {printers
            ? printers.map((printer) => {
                return (
                  <tr key={printer.address}>
                    <td>{printer.location}</td>
                    <td>
                      <a
                        href={"//" + printer.address}
                        target="_blank"
                        rel="noopener noreferrer"
                      >
                        {printer.address}
                      </a>
                    </td>
                    <td>{printer.name}</td>
                    <td>{printer.serial}</td>
                    <td>{printer.black > -1 ? printer.black : ""}</td>
                    <td>{printer.yellow > -1 ? printer.yellow : ""}</td>
                    <td>{printer.magenta > -1 ? printer.magenta : ""}</td>
                    <td>{printer.cyan > -1 ? printer.cyan : ""}</td>
                    <td>{printer.k1 > -1 ? printer.k1 : ""}</td>
                    <td>{printer.k2 > -1 ? printer.k2 : ""}</td>
                    <td>{getPrints(printer)}</td>
                    <td>{getCopies(printer)}</td>
                  </tr>
                );
              })
            : null}
        </tbody>
      </table>
    </div>
  );
}

Services/ PrinterServices

import axios from "axios";
import { useEffect, useState } from "react";

const PRINTER_REST_API_URL = "http://localhost:8080/GetReport";

export interface Printer {
  address: string;
  black: number;
  monoCopies: number;
  monoPrints: number;
  colourCopies: number;
  colourPrints: number;
  cyan: number;
  k1: number;
  k2: number;
  location: string;
  magenta: number;
  name: string;
  serial: string;
  yellow: number;
  Reports: [
    {
      month: string;
      monoPrints: number;
      monoCopies: number;
      colourPrints: number;
      colourCopies: number;
    }
  ];
}

export function GetPrinters() {
  const [printers, setPrinters] = useState<Printer[] | null>();
  useEffect(() => {
    axios.get(PRINTER_REST_API_URL).then((response) => {
      setPrinters(response.data);
    });
  }, []);
  return printers;
}

EDIT: Typescript of the code that worked from serod:

const Context = createContext<Printer[] | undefined>(undefined);

interface ContextProviderProps {
  children: ReactNode;
}

export const ContextProvider: React.FC<ContextProviderProps> = ({
  children,
}) => {
  const [printers, setPrinters] = useState<Printer[]>();
  useEffect(() => {
    axios.get(PRINTER_REST_API_URL).then((response) => {
      setPrinters(response.data);
    });
  }, []);

  return <Context.Provider value={printers}>{children}</Context.Provider>;
};

Solution

  • You can use "React Context" to store the array on a global state and then access it from any component.

    Context code would look like this in Javascript:

    import React, { useState, createContext, useEffect } from "react";
    import axios from "axios";
    
    const Context = createContext();
    
    export const ContextProvider = ({ children }) => {
      const [printers, setPrinters] = useState();
      useEffect(() => {
        axios.get("PRINTER_REST_API_URL").then((response) => {
          setPrinters(response.data);
        });
      }, []);
    
      return (
        <Context.Provider
          value={{
            printers,
          }}
        >
          {children}
        </Context.Provider>
      );
    };
    
    export default Context;
    

    Then add ContextProvider in your app.js

    import { BrowserRouter, Routes, Route } from "react-router-dom";
    import { TonerLevels } from "./pages/TonerLevels";
    import { MonoPrints } from "./pages/MonoPrints";
    import { MonoCopies } from "./pages/MonoCopies";
    import { ColourPrints } from "./pages/ColourPrints";
    import { ColourCopies } from "./pages/ColourCopies";
    
    export function App() {
      return (
        <ContextProvider>
          <BrowserRouter>
            <Routes>
              <Route path="/TonerLevels" element={<TonerLevels />}></Route>
              <Route path="/MonoPrints" element={<MonoPrints />}></Route>
              <Route path="/MonoCopies" element={<MonoCopies />}></Route>
              <Route path="/ColourPrints" element={<ColourPrints />}></Route>
              <Route path="/ColourCopies" element={<ColourCopies />}></Route>
            </Routes>
          </BrowserRouter>
        </ContextProvider>
      );
    }
    

    Now you can simply use useContext hook to access the printers state from any component.