Create App
npm install create-react-app
create-react-app my-app
App.js
function App() {
return (
<div className="App">
<h1>Lets get Started</h1>
<ExpenseItem />
<ExpenseItem />
<ExpenseItem />
</div>
);
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<App />
);
<ExpenseItem title={title} date={date} />
function ExpenseItem(props) {
const title = props.title;
const date = props.date;
}
<Card />
<ExpenseItem />
<div></div>
</Card>
<div>
{props.children}
</div>
<Card className="expense-item">
</Card>
function Card(props) {
// This line right here
const classes = 'card '+ props.className;
return (
<div className={classes}>
{props.children}
</div>
)
}
export default Card;
<div className={'form-control valid'}></div>
<div className={`form-control ${valid ? 'green': 'red'}`}></div>
<div className={`form-control ${validClass}`}></div>
Remember: Custom Components are not seen in DOM
<p style={{height: variableName, color: 'red'}}>Some Data</p>
<p style={{color: !isValid ? 'green': 'red'}}>Some Data</p>
<button onClick={() => {console.log('Clicked!');}}>
const clickHandler = () => {
console.log('Clicked!');
}
<button onClick={clickHandler}>Change Title</button>
function ExpenseItem(props) {
let title = props.title;
const clickHandler = () => {
title = 'Deepen'
}
}
<div onClick={clickHandler}></div>
This wont re-render the JSX
const [title, setTitle] = useState(props.title);
Must be called inside Component functionand not inside any Handler or outside component function.
const [obj, setObj] = useState({a: 0, b: 0});
const onChange = (event) => {
// setObj({...obj, a: event.target.value}); // Dont do this.
setObj((prevState) => {
return {...prevState, a: event.target.value};
})
}
onSubmit
does not do e.preventDefault
. We need to do it manually.
Parent Component
const saveDataHandler = (data) => {
};
<Expense saveData={saveDataHandler}>
Child Component
props.saveData('someValue'); // just like .emit()
{data.map((e, i) => <div key={i}></div>)}
{data.map((e, i) => <div key={e.id}></div>)}
{someCondition ? <p>Yes</p>: <p>No</p>}
{someCondition && <p>Yes</p>}
let expensesCount = <p>No expenses found.</p>
if(someCondition) {
expenseCount = filterData.map(e => <p>Some value</p>)
}
{expenseCount}
- Use a library called
styled components
- Using CSS Modules
import styles from 'button.module.css'; // Yes, you need to have .module in your filename for it to work.
<button className={styles.button}>
<button className={styles['button']}>
<button className={`${styles['button']} ${!isValid && styles.invalid}`}>
.button {
color: white;
backgroundColor: red;
}
Check DOM element.
Check the link for more reference example
You need to have one root element for JSX to work. To avoid this use fragments.
<React.Fragment>
<h2>Hello</h2>
</React.Fragment>
<>
<h2>Hello</h2>
</>
Just like ng-container
{ReactDOM.createPortal(
<SomeComponent
title={props.title}
message={props.message}
onConfirm={props.onConfirm} />,
document.getElementById('overlay-root')
)}
Just like Template Reference in Angular
const nameRef = useRef();
// Somewhere
console.log(nameRef.current.value);
<input type="text" ref={nameRef} />
import { useEffect, useState } from "react";
export default function Dude() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('count: ', count);
if(count !== 0 && count % 3 === 0) {
console.log('Mod 3!');
}
}, [count]); // setCount will not be tracked by effect, only variables(getters)
const inc = () => {
setCount((prevState) => prevState + 1);
}
return (
<>
<h1>Count: {count}</h1>
<button type="button" onClick={inc}>Increment +</button>
</>
);
}
- You specify variables(getters) in dependencies and it will track those.
- If you do not specify any dependencies ([]), then it will only run once and not on every render.
- If you do not specify dependency at all, then it will execute on every render and it will same you have specified outside of
useEffect
. - You DON'T need to add variables or functions you might've defined OUTSIDE of your components (e.g. if you create a new helper function in a separate file): Such functions or variables also are not created inside of a component function and hence changing them won't affect your components (components won't be re-evaluated if such variables or functions change and vice-versa).
useEffect(() => {
// something
});
There is something called cleanup function
useEffect(() => {
return () => {
console.log('Clean up');
}
}, [someVariable]);
Suppose you want to make a debounce functionality.
useEffect(() => {
const identifier = setTimeout(() => {
console.log('Checking form validity!');
}, 500);
return () => {
console.log('Clean up');
clearTimeout(identifier);
}
});
There are 3 parts while using Context
- Defining the Data(Store)
- Provider
- Consumer
export default const AuthContext = React.creatContext({
isLoggedIn: false
});
import AuthContext from '../auth-context';
return (
<AuthContext.Provider
value={{
isLoggedIn: isLoggedIn // This acts like your selector in your ngrx
// This is the value which will be passed to your consumer.
}}
> // Consider this in your app.component.html
</AuthContext.Provider>
)
<AuthContext.Consumer>
{(ctx) => (
<button>{ctx.isLoggedIn ? 'Logout': 'Login'}</button>
)}
</AuthContext.Consumer>
OR
import React, {useContext} from 'react';
const Navigation = (props) => {
const ctx = useContext(AuthContext);
return (<button>{ctx.isLoggedIn ? 'Logout': 'Login'}</button>);
}
You can also have/reference functions in context.
Context Limitations:
- React Context is NOT optimized for high frequency changes.
- React Context also should NOT be used to replace all component communications and props.
React hooks are nothing words starting with use.
Dont know why to make one?
npm install react-router-dom
Simple Router example,
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import HomePage from './components/Home';
import ProductsPage from './components/Products';
const router = createBrowserRouter([
{
path: '/',
element: <HomePage/>
},
{
path: '/products',
element: <ProductsPage />
}
]);
function App() {
return <RouterProvider router={router} />;
}
export default App;
Home.js
export default function HomePage() {
return <h1>Home Page</h1>
}
Products.js
export default function ProductsPage() {
return <h1>Products Page</h1>
}
OLD APPROACH
const routeDefinitions = createRoutesFromElements(
<Route>
<Route paths="/" element={<HomePage />}>
<Route paths="/products" element={<ProductsPage />}>
</Route>
);
const router = createBrowserRouter(routeDefinitions);
export default function App() {
return <RouterProvider router={router} />;
}
import { Link } from 'react-router-dom';
export default function HomePage() {
return (<>
<h1>Home Page</h1>
<p>
Go to <Link to="/products">The list of products</Link>.
</p>
</>);
}
You can also <a href="/products">
but it will refresh the page.
const router = createBrowserRouter([
{
path: '/',
element: <RootLayout />,
children: [
{
path: '/',
element: <HomePage/>
},
{
path: '/products',
element: <ProductsPage />
}
]
}
]);
For Error Page.
const router = createBrowserRouter([
{
path: '/',
element: <RootLayout />,
children: [
{
path: '/',
element: <HomePage/>
},
{
path: '/products',
element: <ProductsPage />
}
],
errorElement: <Error />
}
]);
<NavLink to="/" className={({isActive}) => isActive ? 'active': undefined}>Home</NavLink>
end
will match at the end of to="/"
. Why so? because by default it will make it active even if its children route. To avoid this, you can use end property.
<NavLink to="/" className={({isActive}) => isActive ? 'active': undefined} end>Home</NavLink>
export default function HomePage() {
const navigate = useNavigate();
function navigateHandler() {
navigate('/products');
}
return (
<>
<button onClick={navigateHandler}></button>
</>
);
}
const router = createBrowserRouter([
{
path: '/products/:productId',
element: <ProductDetailPage />
}
]);
import { useParams } from 'react-router-dom';
export default function ProductDetailPage() {
const params = useParams();
const prodId = params.productId && params.productId.toUpperCase();
return (
<>
<h1>Product Details!</h1>
<p>{prodId}</p>
</>
);
}
Above are absolute paths starting with '/'. If you remove starting '/', it will be relative path.
Both in route config and to
property in <Link>
.
You can add relative
to <Link>
for different parent path.
Go one path back:
<Link to="..">Back</Link>
relative="path"
relative="route"
If you put path
, then it will remove from end of the path.
{
index: true, // default route for '/path'
element: <HomePage />
}
{
index: true,
element: <HomePage />,
loader: async () => {
const response = await fetch('http://localhost:8080/events');
if(!response.ok) {
//..
} else {
const resData = await response.json();
return resData.events; // which contains array of data
}
}
}
How to use it?
import { useLoaderData } from 'react-router-dom';
export function HomePage() {
const data = useLoaderData();
}
This will not load Component until data is resolved.
Show loading state.
export function HomePage() {
const navigation = useNavigation();
return (
<>
{navigation.state == 'loading' && <p>...Loading</p>}
</>
);
}
You can access params in loader
export async function loader({events, params}) {
const id = params.eventId;
}
Centralized page for errors.
Consider, you there is an error in loader
where api fails, so if you throw response
then you can get data from error object.
export async function loader() {
const respnonse = await fetch('http://someurl');
if(!response.ok) {
throw new Response(JSON.stringify({message: 'Could not fetch events.'}), {
status: 500
}); // Creating Custom Response from frontend
} else {
return reponse;
}
}
You can also use json()
from 'react-router-dom' which creates a json response.
const error = useRouteError();
If you have loader in your parent and want to refer to that loader then,
{
path: 'event',
id: 'event-detail',
children: [
{
path: ':eventId',
...
}
]
}
import { useRouteLoaderData } from 'react-router-dom';
export function EditEventPage() {
const data = useRouteLoaderData('event-detail');
}
?