📝 Notes for Learning React #7
-
😀 Contents are about Prop Drilling, useContext, Custom Hook, and PropTypes.
-
🚨 It is written only for myself (It may have no reference value) -
🔗 Source code in my GitHub
👇🏻 Prop Drilling
It refers to the process of passing data down through multiple layers of components in a component tree by passing props explicitly to each intermediate component, even if some of them don't actually need the data.
💡 Ok this is the scenario:
Look at this example, if we are going to pass the
removePersonfunction toSinglePersoncomponent, what we are going to do?We are going to set props over and over again, right? (This is the "prop drilling")
import React, { useState } from 'react';
import { data } from '../../../data';
// more components
// fix - context api, redux (for more complex cases)
const PropDrilling = () => {
const [people, setPeople] = useState(data);
const removePerson = id => {
setPeople(people => {
return people.filter(people => people.id !== id);
});
};
return (
<section>
<h3>prop drilling</h3>
<List people={people} removePerson={removePerson} />
</section>
);
};
const List = ({ people, removePerson }) => {
return (
<>
{people.map(person => {
return (
<SinglePerson
key={person.id}
{...person}
removePerson={removePerson}
/>
);
})}
</>
);
};
const SinglePerson = ({ id, name, removePerson }) => {
return (
<div className="item">
<h4>{name}</h4>
<button onClick={() => removePerson(id)}>remove</button>
</div>
);
};
export default PropDrilling;
👇🏻 useContext
useContext is a hook that allows components to consume data from a context without having to pass it down through intermediate components in the component tree.
In other words, useContext provides a way for a component to access data or functionality that has been defined in a higher-level component, without having to pass it down as a prop through every intermediate component.
Here is the example that we refractor the code above:
import React, { useState, useContext } from 'react';
import { data } from '../../../data';
// more components
// fix - context api, redux (for more complex cases)
const PersonContext = React.createContext();
// two components - Provider, Consumer
// `ContextAPI` is our parent component
const ContextAPI = () => {
const [people, setPeople] = useState(data);
const removePerson = id => {
setPeople(people => {
return people.filter(person => person.id !== id);
});
};
return (
// `PersonContext.Provider` component is for providing the context data to its children.
<PersonContext.Provider value={{ removePerson, people }}>
<h3>Context API / useContext</h3>
<List />
</PersonContext.Provider>
);
};
const List = () => {
// It is for a child component to retrieve the data ({removePerson, people})
const mainData = useContext(PersonContext);
console.log(mainData);
return (
<>
{mainData.people.map(person => {
return <SinglePerson key={person.id} {...person} />;
})}
</>
);
};
const SinglePerson = ({ id, name }) => {
const { removePerson } = useContext(PersonContext);
console.log(data);
return (
<div className="item">
<h4>{name}</h4>
<button onClick={() => removePerson(id)}>remove</button>
</div>
);
};
export default ContextAPI;
Using useContext can make your code more concise and easier to read, as it eliminates the need to pass data through multiple levels of components. However, it should be used judiciously, as using too many contexts can make your code harder to understand and maintain.
👇🏻 Custom Hooks - useFetch
It is the example to fetch a url with customized hook - useFerch:
// 1-fetch-example.js
import React, { useState, useEffect } from 'react';
import { useFetch } from './2-useFetch';
// ATTENTION!!!!!!!!!!
// I SWITCHED TO PERMANENT DOMAIN
const url = 'https://course-api.com/javascript-store-products';
const Example = () => {
const { loading, products } = useFetch(url);
console.log(products);
return (
<div>
<h2>{loading ? 'loading...' : 'data'}</h2>
</div>
);
};
export default Example;
This is the useFetch.js
import { useState, useEffect } from 'react';
// Custom hook
export const useFetch = url => {
const [loading, setLoading] = useState(true);
const [products, setProducts] = useState([]);
const getProducts = async () => {
const response = await fetch(url);
const products = await response.json();
setProducts(products);
setLoading(false);
};
// Call the `useEffect` when to `url` changed
useEffect(() => {
getProducts();
}, [url]);
return { loading, products };
};
👇🏻 PropTypes
PropTypes is a mechanism in React for checking the types of props passed to a component. It helps to ensure that the correct types of data are passed to the component and can help to prevent bugs and errors in your application.
Think about a scenario:
import React from 'react';
const Product = ({ image, name, price }) => {
return (
<article className="product">
<img src={image.url} alt={name} />
<h4>{name}</h4>
<p>${price}</p>
</article>
);
};
export default Product;
This is a product.js which fetched the data from API.
BUT, what if the API missing some properties here? (like image source, price or sth).
We will get a big fat error from our project.
How do we fix it?
- Use
PropTypesprovided by React (deprecated from React-v15):
import React from 'react';
import PropTypes from 'prop-types';
const Product = ({ image, name, price }) => {
console.log(image, name, price);
return (
<article className="product">
<h4>single product</h4>
{/* <img src={image.url} alt={name} />
<h4>{name}</h4>
<p>${price}</p> */}
</article>
);
};
Product.propTypes = {
image: PropTypes.object.isRequired,
name: PropTypes.string.isRequired,
price: PropTypes.number.isRequired,
};
export default Product;
- We can also use
defaultPropsto fix it:
import React from 'react';
import PropTypes from 'prop-types';
import defaultImage from '../../../assets/default-image.jpeg';
const Product = ({ image, name, price }) => {
console.log(image, name, price);
return (
<article className="product">
<h4>single product</h4>
<img src={image.url} alt={name} />
<h4>{name}</h4>
<p>${price}</p>
</article>
);
};
Product.defaultProps = {
name: 'default name',
price: 3.99,
image: { url: defaultImage },
};
export default Product;
✉️ Contact me anyway if I get somethings wrong here.