- Library are like pieces of furniture that add style and function to an already constructed house
- Frameworks, on the other hand, are a template you use to build the house itself.
- JSX (Javascript XML)
- Components (pure functions)
- state
- props
- Virtual DOM
- changes happens virtually before replacing actual DOM (lightweight)
- Framework
- NextJS (create-next-app)
Create a boilerplate using create-next-app
- Create a
pagesfolder undersrc - Create an
index.tsxfile undersrc/pageswith this content:export default function Home() { return ( <div> <h1>Home</h1> </div> ); }
- `pages`` folder servers as routing for NextJS
index.tsxalways serves as the displayed page in the route- it's important to ALWAYS use
export defaultin page components (otherwise NextJS will not see it as a page)
- Create another folder inside
pagesnamedtwistand anotherindex.tsxundersrc/pages/twistwith content:export default function Twist() { return ( <div> <h1>Twist</h1> </div> ); }
- In your browser, navigate to
localhost:3000/twist - Create another file inside
src/pages/twistnamedresources.tsxwith content:export default function Resources() { return ( <div> <h1>Resources</h1> </div> ); }
-
In
src/pages, create a file named_app.tsx_app.tsxserves as the root file for all pages, anything displayed here will be displayed in all pages
import { AppProps } from "next/app"; import Link from "next/link"; export default function App({ Component }: AppProps) { return ( <div> <div style={{ display: "flex", gap: "12px" }}> <Link href="/">Home</Link> <Link href="/twist">Twist</Link> <Link href="/resources">Resources</Link> </div> <Component /> </div> ); }
<Component />is the component page rendered in each route (e.g.src/pages/twist.tsx)
-
Try clicking the links at the top of your webpage to see how it works
- Create a
componentsfolder insidesrc - Create a file named
button.tsxinsidesrc/componentswith content:export const Button = () => { return <button>Click me</button>; };
- Import the
Buttoncomponent in your homepage and place it below theHello- components can be
- self-closed
<Button />
- paired
<Button></Button>
- self-closed
- check if it is rendered in your homepage
- NextJS handles aliased imports
@/components/whatever
- components can be
- object that triggers a re-render
- basically short for
property
-
In your Home page file(
src/pages/index.tsx), create a state to count the number of times a button is clicked- place it above the
returnstatement
const [clicks, setClicks] = useState(0);
- don't forget to import
useState
- place it above the
-
Go to
src/components/button.tsx, and declare yourpropstype (used to be PropTypes in base ReactJS)type ButtonProps = { onClick: () => void, };
-
Add the type declaration to your component params and deconstruct your
props:- you should see an intellisense for your
onClickmethod when you click CTRL + Space in your keyboard
export const Button = ({ onClick }: ButtonProps) => {
- you should see an intellisense for your
-
Assign the
onClickprop to your button'sonClickevent listener prop:return <button onClick={onClick}>Click me</button>;
-
Go back to
src/pages/index.tsxand assign thesetClicksmethod to your button'sonClickprop, and show theclicksstate above the button<p>Clicks: {clicks}</p> <Button onClick={() => setClicks(clicks + 1)} />
-
Click the
Click Mebutton a couple of times and see how the count reacts
state can be handled in many ways:
- useState
- useContext
- useReducer
- Redux
- etc.
In this example, we're going to explore where to best place the state and which state management tool to use best
-
used to expose state in a given context
-
solution for
prop drilling-
passing state from component to component until you reach the component where you want to use that state
-
example: passing the
countprop in this manner- _app.tsx
-
const [count] = useState(); return <Component count={count}>
- index.tsx
-
const Home = ({ count }) => ( <Header count={count}> )
- header.tsx
-
const Header = ({ count }) => ( <Content count={count}> )
- content.tsx
-
const Content = ({ count }) => <p>{count}</p>;
-
instead, we can just do this:
-
_app.tsx
-
export const CountContext = createContext(0); function App() { const [count] = useState(); return ( <CountContext.Provider value={clicks}> <div> <h1>Home</h1> <p>Clicks: {clicks}</p> <Button onClick={() => setClicks(clicks + 1)} /> </div> </CountContext.Provider> ); }
-
content.tsx
-
import { CountContext } from "@/pages"; const Content = () => { const count = useContext(CountContext); return <p>{count}</p>; };
-
-
useReducer is a clone of Redux
- state
- action
- type
- payload
- used to promote logic and state reusability
- hooks are basically
componentsthat returns whatever you want
example:
in src/hooks/useCount.tsx
import { useState } from "react";
export const useCount = ({ defaultCount = 0 }: { defaultCount?: number }) => {
const [count, setCount] = useState(defaultCount);
return { count, setCount };
};in src/pages/index.tsx,
instead of declaring:
const { clicks, setClicks } = useCount({ defaultCount: 1 });we can import the useCount hook instead
import { useCount } from "@/hooks/useCount";
export default function HOME() {
const { clicks, setClicks } = useCount({ defaultCount: 1 });Frameworks I've used:
- Material UI
- probably the most famous UI library
- Chakra-UI
- simple and has a TailwindCSS like way of adding styles
- TailwindCSS
- used to just add classNames of predefined CSS attributes
Things to consider when choosing your UI library
- Mobile-friendly
- Accessibility
- aria-roles
- aria-labels
- important when unit testing
- Support
7. Axios
- HTTP client for node and browser
Create only 1 axios instance for consistency and just import the created axios instance
const api = axios.create({
baseURL: "localhost:3000/api",
});- Cypress
- more versatile
- React Testing Library
- for unit testing