Make sure to run npm i
to install the dependencies and npm start
to start the app
Lets start with routing!
There are 3 components/pages in the pages
directory, we'll be adding react router to our app in order to make these pages navigable.
- Firstly we need to install react router -
npm i react-router-dom
- Then we must import the 3 relevant routing components from react-router-dom into the
App.js
file - Finally, we need to nest each of the imported components within each other and ensure each component/page is navigable at an appropriately named
path
for eachelement
i.e. page
Solution
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
...
<Router>
<Routes>
<Route element={<Home />} path='/' />
<Route element={<About img={img} />} path='/about' />
<Route element={<Contact img={img} />} path='/contact' />
</Routes>
</Router>
...
You should now be able to use the url address bar to get to all of your pages.
Also - delete the 'App component' text in the App.js as we'll no longer need it.
We can use the address bar to get around the app, but it would be better if we could use a real nav.
- Add a
nav
jsx element inside of your router but before your routes - Inside the nav, add an unorganised list - containing 3 list items
- Each list item will contain an element that will is imported from react router dom, can you remember what this called?
- Add the
nav
className to the nav html element
Solution
<nav className="nav">
<ul>
<li><NavLink to='/'>Home</NavLink></li>
<li><NavLink to='about'>About</NavLink></li>
<li><NavLink to='contact'>Contact</NavLink></li>
</ul>
</nav>
We're not going to do much with the home page, in fact it will be in a 'perpetual state of loading'...
- Import the
Loading
component into theHome
page and add the classnamehome
to the top level container in the Home component
Solution
<div className='home'>
<h1>Home page</h1>
<Loading />
</div>
We'll now make an api call and display an image
- In the
App.js
create an image state variable, and an appropriately named function to update the image state (what React hook would we want to do this?) - Pass the image as a prop to the About and Contact pages
- In the About page destructure the
image
prop - Conditionally display the image if the prop is 'truthy'
- Repeat steps 3 - 4 for the Contact page
Solution step 1
const [img, setImg] = useState('');
Solution step 2
<Route element={<Home />} path='/' />
<Route element={<About img={img} />} path='/about' />
<Route element={<Contact img={img} />} path='/contact' />
Solution step 3
const About = ({img}) => {
...
Solution step 4
<h1>About page</h1>
{img && <img src={img} alt='about' />}
In the data folder, we have some info for the about page. Have a look at how the data is structured
- Import this data into the About page
- Create some
aboutData
state and use the imported data as the initial state .map
theaboutData
state below the image within the return statement of the about component. Render abutton
for each title and ap
tag to display the description for each item in the array if the visible property is truthy (Add an empty event handler prop to the button for now)- Create an event handler function called
handleClick
- Within this function you will need an intermediate variable, create a variable called
newData
newData
should store the result of mapping the aboutData state to search for a matching id which will in turn toggle the section's visibility- Name the parameter of the cb function
data
- This function will compare the
data.id
with theid
of the aboutSection data that is passed to it from the trigger buttin element - If the id matches we should toggle the visibility of the element using the bang operator
!
or else return thedata
as is - We then need to
setAboutData
with the newly updatednewData
(This will happen each time an about section button is clicked) - Make sure to update the button event handler so the handler function is called with an aboutSection id
Solution step 1
import { data } from '../data/aboutInfo'
Solution step 2
const [aboutData, setAboutData] = useState(data)
Solution step 3
{aboutData.map(aboutSection => (
<>
<button onClick={() => {}}>{aboutSection.title}</button>
<p>{aboutSection.visible && aboutSection.description}</p>
</>
))}
Solution step 4
const handleClick = (id) => {
const newData = aboutData.map(data => data.id === id ? ({...data, visible: !data.visible }) : data)
setAboutData(newData)
}
...
<button onClick={() => handleClick(aboutSection.id)}>{aboutSection.title}</button>
Stretch: In your own time add a css transition to make toggling the visibility in the UI smoother
- Create some
formData
state - Add a form html element, with an empty onSubmit handler
- Add an input and a button element within the form. The button should be of type submit and contain the text submit
- The input should be of type text and have an onChange handler, set the value of this handler to
handleChange
- We need to create this handler, it will need to access the event target's value. We will use this to
setFormData
when the input's value changes - Back to the overall form's submit handler, we will now create a
handleSubmit
function
- This function will need to prevent the default behaviour when submitting forms, what is this?
- In a real application we might use axios to post formata to a database but we will just console.log it instead 
Solution step 1
const [formData, setFormData] = useState()
Solution step 2
<form onSubmit={() => {}}>
...
</form>
Solution step 3 / 4
<form onSubmit={() => {}}>
<input type='text' onChange={handleChange} />
<button type='submit'>Submit</button>
</form>
Solution step 5
const handleChange = (e) => {
setFormData(e.target.value)
}
Solution step 6
const handleSubmit = (e) => {
e.preventDefault()
console.log(formData)
}