initial
This commit is contained in:
commit
74ae93a96e
1 changed files with 94691 additions and 0 deletions
|
|
@ -0,0 +1,508 @@
|
|||
Coding 9 (Composition Patterns)
|
||||
|
||||
Server Components
|
||||
- Fetching data
|
||||
- Directly accessing backend resources
|
||||
- Protecting sensitive information (like access tokens and API keys) on the server
|
||||
- Keeping large dependencies server-side helping in reducing client side javascript
|
||||
|
||||
|
||||
Client Components
|
||||
- Adding interactivity and handling event listeners
|
||||
- Managing state and lifecycle effects ( useState(), useReducer(), useEffect() )
|
||||
- Using browser-exclusive APIs
|
||||
- Custom hooks
|
||||
- React Class components
|
||||
|
||||
|
||||
## Server-only code
|
||||
- Its not wise to include server side code to prevent exposing logic, api keys or data also it may not work on client too
|
||||
- So its important to have a clear boundary
|
||||
- Create server side functions in different files and import `server-only` module in that file(containing server side function) which will give error at build time if its code is used in client components
|
||||
```text
|
||||
src/app
|
||||
utils
|
||||
server-utils.jsx
|
||||
server-route
|
||||
page.jsx
|
||||
client-route
|
||||
page.jsx
|
||||
```
|
||||
|
||||
In `src/app/utils/server-utils.jsx`
|
||||
```js
|
||||
import "server-only";
|
||||
|
||||
export const ServerSideFunction = () => {
|
||||
console.log(
|
||||
`use multiple libraries,
|
||||
use environment variables,
|
||||
interact with a database,
|
||||
process confidential information`
|
||||
);
|
||||
|
||||
return "server result";
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
In `src/app/server-route/page.jsx`
|
||||
```js
|
||||
import { ServerSideFunction } from "../utils/server-utils";
|
||||
|
||||
export default function ServerRoutePage() {
|
||||
console.log("Server router rendered");
|
||||
|
||||
const result = ServerSideFunction();
|
||||
return (
|
||||
<>
|
||||
<h1>ServerRoutePage</h1>
|
||||
<p>{result}</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
In `src/app/client-route/page.jsx`
|
||||
```js
|
||||
"use client";
|
||||
|
||||
import { ServerSideFunction } from "../utils/server-utils";
|
||||
|
||||
export default function ClientRoutePage() {
|
||||
console.log("Client route rendered");
|
||||
const result = ServerSideFunction();
|
||||
return (
|
||||
<>
|
||||
<h1>ClientRoutePage</h1>
|
||||
<p>{result}</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
**Third Party Packages**
|
||||
- third party packages in npm dont use 'use client' directive so they cant be used in server components so we need to encapsulate them in another client component and use it instead
|
||||
- It is possible to use nested client side component inside server side component
|
||||
Example : Using `react-slick` package
|
||||
|
||||
Structure
|
||||
```text
|
||||
src/app
|
||||
components
|
||||
ImageSlider.jsx
|
||||
ImageSlider.css
|
||||
utils
|
||||
server-utils.jsx
|
||||
server-route
|
||||
page.jsx
|
||||
client-route
|
||||
page.jsx
|
||||
```
|
||||
|
||||
In `src/app/components/ImageSlider.jsx`
|
||||
```js
|
||||
"use client";
|
||||
|
||||
// import { ServerSideFunction } from "../utils/server-utils";
|
||||
|
||||
import Slider from "react-slick";
|
||||
|
||||
// Import css files
|
||||
import "slick-carousel/slick/slick.css";
|
||||
import "slick-carousel/slick/slick-theme.css";
|
||||
import "./ImageSlider.css";
|
||||
|
||||
export default function ImageSlider() {
|
||||
console.log("Client route rendered");
|
||||
// const result = ServerSideFunction();
|
||||
|
||||
var settings = {
|
||||
dots: true,
|
||||
infinite: true,
|
||||
speed: 500,
|
||||
slidesToShow: 1,
|
||||
slidesToScroll: 1,
|
||||
};
|
||||
|
||||
return (
|
||||
// <>
|
||||
// <h1>ClientRoutePage</h1>
|
||||
// <p>{result}</p>
|
||||
// </>
|
||||
|
||||
// <Slider {...settings}>
|
||||
<Slider {...settings}>
|
||||
<div className="fit-con">
|
||||
<img src="https://picsum.photos/id/34/367/267" alt="picsum" />
|
||||
</div>
|
||||
<div className="fit-con">
|
||||
<img src="https://picsum.photos/id/35/367/267" alt="picsum" />
|
||||
</div>
|
||||
<div className="fit-con">
|
||||
<img src="https://picsum.photos/id/37/367/267" alt="picsum" />
|
||||
</div>
|
||||
<div className="fit-con">
|
||||
<img src="https://picsum.photos/id/41/367/267" alt="picsum" />
|
||||
</div>
|
||||
<div className="fit-con">
|
||||
<img src="https://picsum.photos/id/40/367/267" alt="picsum" />
|
||||
</div>
|
||||
<div className="fit-con">
|
||||
{/* <img src="https://picsum.photos/id/39/367/267" alt="picsum" /> */}
|
||||
<h1>6</h1>
|
||||
</div>
|
||||
</Slider>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
In `src/app/components/ImageSlider.css`
|
||||
|
||||
```css
|
||||
.fit-con {
|
||||
display: flex !important;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
In `src/app/server-route/page.jsx`
|
||||
```js
|
||||
import ImageSlider from "../components/ImageSlider";
|
||||
import { ServerSideFunction } from "../utils/server-utils";
|
||||
|
||||
export default function ServerRoutePage() {
|
||||
console.log("Server router rendered");
|
||||
|
||||
const result = ServerSideFunction();
|
||||
return (
|
||||
<>
|
||||
<h1>ServerRoutePage</h1>
|
||||
<p>{result}</p>
|
||||
<div style={{ width: "367px", margin: "auto" }}>
|
||||
<ImageSlider />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
**Context Providers**
|
||||
- Context providers are typically rendered near the root of an application to share global application state and logic. Eg : App theme
|
||||
- React context is not supported in Server Compinent so just like encapusalating third package in separate client component we need to do same for this
|
||||
|
||||
Structure
|
||||
```text
|
||||
src/app
|
||||
layout.jsx
|
||||
client-route
|
||||
page.jsx
|
||||
components
|
||||
theme-provider.jsx
|
||||
|
||||
```
|
||||
|
||||
In `src/app/components/theme-provider.jsx`
|
||||
|
||||
```js
|
||||
"use client";
|
||||
|
||||
import { createContext, useContext } from "react";
|
||||
|
||||
const defaultTheme = {
|
||||
colors: {
|
||||
primary: "#007bff",
|
||||
secondary: "#6c757d",
|
||||
},
|
||||
};
|
||||
|
||||
const ThemeContext = createContext(defaultTheme);
|
||||
|
||||
export const ThemeProvider = ({ children }) => {
|
||||
return (
|
||||
<ThemeContext.Provider value={defaultTheme}>
|
||||
{children}
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useTheme = () => useContext(ThemeContext);
|
||||
export { ThemeContext };
|
||||
|
||||
```
|
||||
|
||||
In `src/app/layout.jsx`
|
||||
|
||||
```js
|
||||
import { Inter } from "next/font/google";
|
||||
import { ThemeProvider } from "./components/theme-provider";
|
||||
|
||||
|
||||
|
||||
|
||||
export const metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<ThemeProvider>
|
||||
<body>{children}</body>
|
||||
</ThemeProvider>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
In `src/app/client-route/page.jsx`
|
||||
|
||||
```js
|
||||
"use client";
|
||||
|
||||
import { useContext } from "react";
|
||||
import { ThemeContext } from "../components/theme-provider";
|
||||
|
||||
export default function ClientRoutePage() {
|
||||
const theme = useContext(ThemeContext);
|
||||
|
||||
return (
|
||||
<h1
|
||||
style={{
|
||||
color: theme.colors.secondary,
|
||||
}}
|
||||
>
|
||||
Client Route
|
||||
</h1>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
To update the context value, use useState and transfer state, stateChanger t value of context provider
|
||||
```js
|
||||
import ReactDOM from "react-dom";
|
||||
import React, { useState } from "react";
|
||||
|
||||
import authContext from "./authContext";
|
||||
import Login from "./Login";
|
||||
|
||||
const App = () => {
|
||||
const [authenticated, setAuthenticated] = useState(false);
|
||||
|
||||
return (
|
||||
<authContext.Provider value={{ authenticated, setAuthenticated }}>
|
||||
<div> user is {`${authenticated ? "" : "not"} authenticated`} </div>
|
||||
<Login />
|
||||
</authContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById("container"));
|
||||
```
|
||||
|
||||
|
||||
## Client-only Code
|
||||
- To prevent unintended server side usage of client code we can install and import package `client-only`
|
||||
|
||||
|
||||
Client Component Placement
|
||||
- It is recommended to position client components lower in the component tree
|
||||
- Once the component is client component all the child component becomes client components too
|
||||
```text
|
||||
src/app
|
||||
components
|
||||
navbar.jsx
|
||||
navlink.jsx
|
||||
navsearch.jsx
|
||||
landing-page
|
||||
page.jsx
|
||||
```
|
||||
|
||||
In components
|
||||
```js
|
||||
|
||||
// navlink.jsx
|
||||
export default function Navlink() {
|
||||
console.log("Navlink loaded");
|
||||
|
||||
return <div className="Main">NavLink</div>;
|
||||
}
|
||||
|
||||
//navsearch.jsx
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
export default function NavSearch() {
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
console.log("NavSearch loaded2");
|
||||
|
||||
return <div className="Main">NavSearch</div>;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//navbar.jsx
|
||||
import Navlink from "./navlink";
|
||||
import NavSearch from "./navsearch";
|
||||
|
||||
export default function NavBar() {
|
||||
console.log("navbar loaded");
|
||||
return (
|
||||
<>
|
||||
<div className="">NavBar</div>
|
||||
<Navlink />
|
||||
<NavSearch />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
- useState hook can be used in navbar too but since its usage is required only in navsearch file so its best to use it there otherwise navbar needs to be a client component and navlink will be client component too
|
||||
|
||||
In `src/app/landing-page.jsx`
|
||||
```js
|
||||
import NavBar from "../components/navbar";
|
||||
|
||||
export default function LandingPage() {
|
||||
return (
|
||||
<div className="">
|
||||
<h1>Landing Page</h1>
|
||||
<NavBar />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
**Interleaving Server and Client components**
|
||||
- Server component cannot be directly used inside client component as they are automatically converted to client component by default
|
||||
- So a workaround is needed for this ie passing server component as prop to client component
|
||||
|
||||
```text
|
||||
src/app
|
||||
interleaving
|
||||
page.jsx
|
||||
components
|
||||
client-component.jsx
|
||||
server-component.jsx
|
||||
```
|
||||
|
||||
In `src/app/server-component.jsx`
|
||||
```js
|
||||
import fs from "fs";
|
||||
|
||||
|
||||
export default function ServerComponent() {
|
||||
console.log("ServerComponent loaded");
|
||||
|
||||
const fileBuffer = fs.readFileSync("package.json");
|
||||
|
||||
return (
|
||||
<div className="ServerComponent">
|
||||
<h1>Server Component</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Following wont work and will give error that fs is not working as server component which is children of client component becomes client component
|
||||
|
||||
In `src/app/interleaving.jsx`
|
||||
```js
|
||||
import ClientComponent from "../components/client-component";
|
||||
|
||||
|
||||
export default function InterleavingPage() {
|
||||
console.log("InterleavingPage loaded");
|
||||
|
||||
return (
|
||||
<div className="InterleavingPage">
|
||||
<ClientComponent />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
In `src/app/client-component.jsx`
|
||||
```js
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import ServerComponent from "./server-component";
|
||||
|
||||
export default function ClientComponent() {
|
||||
console.log("client component loaded");
|
||||
|
||||
const [state, seyState] = useState();
|
||||
|
||||
return (
|
||||
<div className="client-component">
|
||||
<h1>Client Component</h1>
|
||||
<ServerComponent />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Passing server component as props instead
|
||||
|
||||
In `src/app/interleaving.jsx`
|
||||
```js
|
||||
import ClientComponent from "../components/client-component";
|
||||
import ServerComponent from "../components/server-component";
|
||||
|
||||
export default function InterleavingPage() {
|
||||
console.log("InterleavingPage loaded");
|
||||
|
||||
return (
|
||||
<div className="InterleavingPage">
|
||||
<ClientComponent>
|
||||
<ServerComponent />
|
||||
</ClientComponent>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
In `src/app/client-component.jsx`
|
||||
```js
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
// import ServerComponent from "./server-component";
|
||||
|
||||
export default function ClientComponent({ children }) {
|
||||
console.log("client component loaded");
|
||||
|
||||
const [state, seyState] = useState();
|
||||
|
||||
return (
|
||||
<div className="client-component">
|
||||
<h1>Client Component</h1>
|
||||
{children}
|
||||
{/* <ServerComponent /> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
Loading…
Reference in a new issue