Day 2
Key Learnings:
pages
folder name is reserved in nextjs.- Others like
components
is not reserved.
- Others like
- How props are passed to a component.
- Utilize
_app.js
for anything that affects all pages like Layout - wrap with Layout component, Navigation (with links) etc - Using Programmatic (Imperative) Navigation
- The usual rule of a Reach Hook:
- Only use directly only at the root level of component function. (e.g. using
useRouter
)
- Only use directly only at the root level of component function. (e.g. using
useRouter
has not justquery
that gives slug, it also haspush()
whose job is equivalent toLink
component
- The usual rule of a Reach Hook:
Starting point of project with some templates
Create a home page with list of cards
Props are passed to a component.
Component NoteList
then uses the notes
prop)
import NoteItem from "./NoteItem";
import classes from "./NoteList.module.css";
function NoteList(props) {
return (
<ul className={classes.list}>
{props.notes.map((note) => (
<NoteItem
key={note.id}
id={note.id}
image={note.image}
title={note.title}
address={note.address}
/>
))}
</ul>
);
}
export default NoteList;
Passing the props to component:
import NoteList from "../components/notes/NoteList";
let note = [
{
id: 1,
image:
"https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg",
title: "sunset",
address: "Beach Hawaii",
},
];
function HomePage() {
return <NoteList notes={note} />;
}
export default HomePage;
Adding a New Note page - where user can add a new note.
Notice the path - its under
pages
But need a form component now!
Create a form component
Use this sample implementation:
- Uses
useRef
(standard React) - Relies on a Prop
onAddNote
to do something, when submit is pressed. - The prop as usual comes from the ? _____
- the parent page
- Page(nextjs) - Component (form)
- Page that contains component - is a style I see in Next or Maxmilian.
- the parent page
import { useRef } from "react";
import Card from "../ui/Card";
import classes from "./NewNoteForm.module.css";
function NewNoteForm(props) {
const titleInputRef = useRef();
const imageInputRef = useRef();
const addressInputRef = useRef();
const descriptionInputRef = useRef();
function submitHandler(event) {
event.preventDefault();
const enteredTitle = titleInputRef.current.value;
const enteredImage = imageInputRef.current.value;
const enteredAddress = addressInputRef.current.value;
const enteredDescription = descriptionInputRef.current.value;
const noteData = {
title: enteredTitle,
image: enteredImage,
address: enteredAddress,
description: enteredDescription,
};
props.onAddNote(noteData);
}
return (
<Card>
<form className={classes.form} onSubmit={submitHandler}>
<div className={classes.control}>
<label htmlFor="title">Note Title</label>
<input type="text" required id="title" ref={titleInputRef} />
</div>
<div className={classes.control}>
<label htmlFor="image">Note Image</label>
<input type="url" required id="image" ref={imageInputRef} />
</div>
<div className={classes.control}>
<label htmlFor="address">Address</label>
<input type="text" required id="address" ref={addressInputRef} />
</div>
<div className={classes.control}>
<label htmlFor="description">Description</label>
<textarea
id="description"
required
rows="5"
ref={descriptionInputRef}
></textarea>
</div>
<div className={classes.actions}>
<button>Add Note</button>
</div>
</form>
</Card>
);
}
export default NewNoteForm;
Import the form (component) in page:
Form (component): Just created above
Page
pages\new-note\index.js
Import and Use:
import NewNoteForm from "../../components/notes/NewNoteForm";
function NewNotePage() {
return <NewNoteForm />;
}
export default NewNotePage;
Something is missing: The prop we need to pass!
import NewNoteForm from "../../components/notes/NewNoteForm";
function NewNotePage() {
function onAddNoteHandler(noteData) {
console.log(noteData);
}
return <NewNoteForm onAddNote={onAddNoteHandler} />;
}
export default NewNotePage;
Learnt from this error -
Error: EISDIR: illegal operation on a directory, read
because had creaed the foldernew-note
by mistake named asnew-note.js
pages\new-note.js\index.js
. The error means node is trying to treat a folder (by mistake) as a file.
Add Layout and Navigation
Layout - to wrap out components Gives a general layout to our components.
Navigation Bar - for links
Use this ready template for Layout and Navigation as reference.
Adding Layout
Original pages\_app.js
:
import '../styles/globals.css'
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
Adding Layout:
import "../styles/globals.css";
import Layout from "../components/layout/Layout";
function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
);
}
export default MyApp;
Add Navigation bar
Its part of Layout! so by adding to _app.js
, navigation also gets added to all pages!
Fix this small pending stuff in MainNavigation.js
- import
Link
- user
href
- Remove margin on top bar
Final!
Programmatic Navigation (Imperative)
We want the buttons to work (Show Details button on home list) On click - Load individual Note item So we would need link to single individual note URL, and can use Link.
We could use the
Link
Component, or<button>
component. But for learning, lets try programmatically changing pages (navigating)
- using
useRouter
: General rule of React: use a react hook directly only at the root level of component function.
Go to index.js > look for components…. >
NoteItem
has the buttonShow Details
Add onClick to
<button>
Write the function - where!?? - got stuck here
a. In parent - and pass in propr and use here from prop?
b. or in same
NoteItem
Had to refer - example chooses to got with b.
Ab kya kare!
Tried this wrong:
import Card from "../ui/Card";
import classes from "./NoteItem.module.css";
import { useRouter } from "next/router";
function NoteItem(props) {
const router = useRouter();
function handleShowDetailsClick(event) {
console.log(event);
let noteId = event.target.id;
router.push(`/${noteId}`);
}
...
}
Solution: Actually nothing to do ith event.target.id
to get the URL.
- We want URL of page.
- We need nothing from button info. We can know the individual card’s (items’s) detail like
key
and since it is used on the URL as slug, we can use it - create URL - push on Router.
Using useRouter to get redirect using push:
The key was passed originally as prop:
…and futher as prop:
We can see that on click, it redirects to the URL! 😁 Although there is no such page yet, so we get an error.
getserversideprops-dynamic-route.gif
Add individual Note component
At /pages/[niteId]/index.js
import { Fragment } from "react";
function Note() {
return (
<Fragment>
<img
src="https://www.nasa.gov/sites/default/files/thumbnails/image/pia23378-16.jpg"
alt="selfie from mars"
></img>
<h1>First Note</h1>
<p>This is Curiosity's selfie from Mars</p>
</Fragment>
);
}
export default Note;
The styling looks off. So create a new STYLING
Naming the css files in a particular way - e.g. say component is
Comp
, then its css file should be namedComp.module.css
. Doing this restricts the scope of the CSS class styles defined toComp
only. Its a React thing, that Next also supports. This is called CSS Modules
Before going into styling let’s try to make everything on this page flexible: Create a pure component - that does everything from its props
By doing this we are also trying to keep pages folder lean
Create function component in components\notes\NoteDetail.js
import classes from "./NoteDetail.module.css";
function NoteDetail(props) {
return (
<segment className={classes.detail}>
<img src={props.image} alt={props.title}></img>
<h1>{props.title}</h1>
<p>{props.details}</p>
</segment>
);
}
export default NoteDetail;
Also create related CSS:
at components\notes\NoteDetail.module.css
.detail img {
/* Makes the large image fit the container */
width: 100%;
}
.detail {
text-align: center;
}
Now pass dummy data as props:
at pages\[noteId]\index.js
import NoteDetail from "../../components/notes/NoteDetail";
function NoteDetails() {
return (
<NoteDetail
title="Selfie from Mars"
details="This is Curiosity's selfie from Mars"
image="https://www.nasa.gov/sites/default/files/thumbnails/image/pia23378-16.jpg"
></NoteDetail>
);
}
export default NoteDetails;