React navigation basics

I just realized that I’ve never made the photos app with react.js. So let’s do that today.

React vs React Native

Setup

I’m going to use vite to build the app with javascript.

npm create vite@latest .
npm install
npm run dev

And we have our app up and running.

setup

Fetch data

To fetch and display data in react is a three step process. First we need useState to hold the data:

const [photoList, setPhotoList] = useState([]);

Then we need a useEffect to fetch the data. One way is to use the promises:

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/photos")
      .then((response) => response.json())
      .then((content) => setPhotoList(content));
  }, []);

Another way is to create an async function and call it from useEffect:

  async function fetchData() {
    const response = await fetch("https://jsonplaceholder.typicode.com/photos");
    const content = await response.json();
    setPhotoList(content);
  }

  useEffect(() => {
    fetchData();
  }, []);

And then draw the UI.

Drawing content

To render the data we can simply use the html unordered list

return (
  <>
    {photoList.length === 0 ? (
      <p>Loading ...</p>
    ) : (
      <ul>
        {photoList.map(({ id, title, thumbnailUrl }) => {
          return (
            <li key={id}>
                <img src={thumbnailUrl} alt={title} />
            </li>
          );
        })}
      </ul>
    )}
  </>
);

photo-list

In the true spirit of react we can probably move the li out as a reusable component and call it PhotoTile:

export function PhotoTile({ title, thumbnailUrl }) {
  return (
    <li className="photoTile">
      <img src={thumbnailUrl} alt={title} />
    </li>
  );
}

and reduce our App.jsx to

<ul className="photoList">
  {photoList.map(({ id, title, thumbnailUrl }) => (
    <PhotoTile key={id} title={title} thumbnailUrl={thumbnailUrl} />
  ))}
</ul>

Next, to make the list 2 columns we can use grid, flexbox or one of the infinite other methods out there, but my favorite is to simply use the column-count

<ul className="photoList"> 
  ...
</ul>


.photoList {
  column-count: 2;
}

two-columns

And now the real deal. The react-router-dom. This changes everything. First we need to install the dependency obviously.

npm install react-router-dom

Then we need to update the main.jsx to use the router

const router = createBrowserRouter([
  {
    path: "/",
    element: <Home />,
  },
]);

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <RouterProvider router={router} />
  </StrictMode>
);

To navigate to the details, we need to register the route with params:

const router = createBrowserRouter([
  {
    path: "/",
    element: <Home />,
  },
  {
    path: "/details/:id",
    element: <Details />,
  },
]);

Then in the Details we can get the param value with useParams() hook

export function Details() {
  const { id } = useParams();
  const [photo, setPhoto] = useState(null);

  async function fetchData(id) {
    const response = await fetch(
      `https://jsonplaceholder.typicode.com/photos/${id}`
    );
    const content = await response.json();
    setPhoto(content);
  }

  useEffect(() => {
    fetchData(id);
  }, [id]);

  return (
    <>
      {!photo ? (
        <Loading />
      ) : (
        <ul>
          <div className="photoDetails">
            <PhotoTile title={photo.title} url={photo.url} />
            <p>{photo.title}</p>
          </div>
        </ul>
      )}
    </>
  );
}

two-columns

And then to navigate between screens we need to make use of the Link component:

export function PhotoTile({ title, url, path }) {
  return (
    <li className="photoTile">
      <Link to={path}>
        <img src={url} alt={title} />
      </Link>
    </li>
  );
}

And then construct the details path from Home screen like:

<PhotoTile
  key={id}
  title={title}
  url={thumbnailUrl}
  path={`/details/${id}`}
/>

And back to home from the Details screen:

<PhotoTile title={photo.title} url={photo.url} path={"/"} />

Conclusion

And there we have the basics of a React app with navigation. The react-router is always evolving, there were some new changes in the v6 which I tried to use. But looks good and works like charm.

The link from this experiment is available at https://github.com/chunkyguy/PhotoApp/tree/master/react.

References