React

Create a project

npm create vite@latest project-folder-name

Shortcuts

// rafce
// rafc
// rfc

Note: Tutorials 1

Note: Tutorials 2

Project: React Jobs (along with Tutorial 2)

  1. create a project, update vite.config.ts

  2. empty app.tsx, delete app.css, and empty index.css

  3. setup tailwind, vite react tailwind

  4. reformat the code copied from the index.html into App.tsx

  5. reformat with component Navbar.tsx

  6. assets/images/logo.png

single page development

  1. props, default values and constraint types (props) => {...} or ({ isHome = false}) => { ... }
  2. useState, onClick={() => setSome((prevState) => !prevState)}

additional packages

  1. react-icon, install additional package npm i react-icons
    • remove
    • import { FaArrowLeft } from 'react-icons/fa'
    • <FaArrowLeft className='mr-2' /> Some Texts
  2. react-router-dom, npm i react-router-dom

routing

  • within App.tsx
import {
  Route,
  createBrowserRouter,
  createRoutesFromElements,
  RouterProvider,
} from `react-router-dom`;
import HomePage from './pages/HomePage';

const router = createBrowserRouter(
  // createRoutesFromElements(<Route index element={<HomePage />} />)
  createRoutesFromElements(<Route path='/about' element={<HomePage />} />)
);

const App = () => {
  return <RouterProvider router={router} />;
}
export default App;

layout

  1. create folder pages, including HomePage, …
  2. within App.tsx, wrap other route with layout route
  3. use Outlet in layout component
  • we prefer link tag, not a tag, because a tag does a complete page refresh
  • How to change a tag to link tag:
    import {Link} from 'react-router-dom'
    
    // 1. change all `a` to `Link`
    // 2. change `href` to `to`

404 page

  • create 404 page
  • add route with *

active link on NavBar

  • use NavLink instead of Link
  • className attached with a function

Conditional rendering

  • use props and ? :

Mock API

  1. use json server with our json file
  2. install: -D means dev dependency npm i -D json-server
  3. update package.json
    • within scripts add "server": "json-server --watch src/jobs.json --port 8888"
  4. run npm run server
  5. use useEffect hook to make a request, we also use useState
    • const [jobs, setJobs] = useState([]);
    • const [loading, setLoading] = useState(true);
    useEffect( () => {
      const fetchJobs = async () => {
        try {
          const res = await fetch('http://localhost:8000/jobs'); // without proxy
          const data = await res.json();
          setJobs(data);
        } catch (error) {
          console.log('Error fetching', error);
        } finally {
          setLoading(false);
        }
      }
    
      fetchJobs();
    }, []);
    
    
    {
      loading ? (
        <Spinner loading={loading} />
      ) : (
        <>
          {jobs.map((job) => (
            <JobListing key={job.id} job={job} />
          ))}
        </JobListing>
      )
    }
  6. use React Spinners, install: npm i react-spinners
    • Ref
    • create a spinner ui component

Different approach to fetch data

  • react suspense, it allows us to do a render while fetching, so we basically provide a fallback UI such as a spinner. (what we are doing here we fetch on render, because when it renders it has a side effect of fetching the data)
  • react query and SWR are third-party libraries. They make data fetching a little easier.
  • react 19 has new use hook

Proxying

  • with create react app use package.json
  • with vite we use vite.config.ts, and add following in server
proxy: {
  '/api': {
    target: 'http://localhost:8000',
    changeOrigin: true,
    rewrite: (path) => path.replace(/^\/api/, ''),
  },
},

// every time we send a request we use /api, i.e. /api/jobs (instead of localhost:00/jobs)

data loader from react router for single page

  • another way to fetch data

the traditional way: useEffet way

import {useState, useEffect} from 'react'; 
import {useParams} from 'react-router-dom';
import Spinner from '../component/Spinner';

const JobPage = () => {
  const { id } = useParams();
  const [job, setJob] = useState(null);
  cont [loading, setLoading] = useState(true);

  useEffect( () => {
    const fetchJob = async () => {
      try {
        const res = await fetch(`/api/jobs/${id}`) // use ` here not '
        const data = await res.json();
        console.log(data);
        setJob(data);
      } catch (error) {
        console.log('Error fetching data', error);
      } finally {
        setLoading(false);
      }
    };

    fetchJob();
  }, []);

  return loading ? <Spinner /> : <h1>{job.title}<h1/>
}

export default JobPage;

new way: data loader way(a new feature with react router not react itself)

// 5.
import {useParams, useLoaderData} from 'react-router-dom'; 

const JobPage = () => {
  const { id } = useParams();

  // 6.
  const job = useLoaderData();
  
  // 7.
  return <h1>{job.title}<h1/>
}

// 1. 
const jobLoader = async ({params}) => {
  const res = await fetch(`/api/jobs/${params.id}`) // use ` here not '
  const data = await res.json();
  return data;
};

// 2. 
export { JobPage as default, jobLoader };
// within App.tsx
// 3. 
import JobPage, {jobLoader} from './pages/JobPage';

// 4. we can pass this jobLoader into other components as well and use that to get a job by its id
...
<Route path='/jobs/:id' element={<JobPage />} loader={jobLoader} />
...

Forms

  • change class to className
  • change for= to htmlFor=

Other ways to work with forms, including foric

  • the most common basic way to work with form:
    • adding a piece of useState for every field in our form.
    • if with react 19: we can use <form action=? >
    • if with older react: we can use <form onSubmit =? >

pass Functions as props && POST request to Add a job

  • we could use context API, we have all request methods in a context file
  • or we could use Redux or some other state manager.
  • within this app we just put all requests within App.tsx

toastify

  • npm i react-toastify