Youcef Madadi
Web and game development teacher
Hooks
React Hooks were introduced to address some of the limitations of functional components and to provide a more cohesive way to handle state and side effects in React applications.
Throw Classes
Widely adopted by major companies and developers worldwide
The useState
Hook is used for adding state to functional components.
const [state, setState] = useState(initialState);
import React, { useState } from 'react';
export function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
import React, { useState } from 'react';
function ToggleButton() {
const [isOn, setIsOn] = useState(false);
const toggle = () => {
setIsOn(!isOn);
};
return (
<div>
<button onClick={toggle}>
{isOn ? 'Turn Off' : 'Turn On'}
</button>
<p>Switch is {isOn ? 'on' : 'off'}.</p>
</div>
);
}
import React, { useState } from 'react';
function InputField() {
const [inputValue, setInputValue] = useState('');
const handleInputChange = (event) => {
setInputValue(event.target.value);
};
return (
<div>
<input
type="text"
value={inputValue}
onChange={handleInputChange}
placeholder="Type something..."
/>
<p>You typed: {inputValue}</p>
</div>
);
}
import React, { useState } from 'react';
function TemperatureConverter() {
const [celsius, setCelsius] = useState(0);
const [fahrenheit, setFahrenheit] = useState(32);
const handleCelsiusChange = (event) => {
const newCelsius = event.target.value;
const newFahrenheit = (newCelsius * 9) / 5 + 32;
setCelsius(newCelsius);
setFahrenheit(newFahrenheit);
};
const handleFahrenheitChange = (event) => {
const newFahrenheit = event.target.value;
const newCelsius = ((newFahrenheit - 32) * 5) / 9;
setFahrenheit(newFahrenheit);
setCelsius(newCelsius);
};
// return part
}
return (
<div>
<label>
Celsius:
<input type="number"
value={celsius}
onChange={handleCelsiusChange}/>
</label>
<br />
<label>
Fahrenheit:
<input type="number"
value={fahrenheit}
onChange={handleFahrenheitChange}/>
</label>
</div>
);
import React, { useState } from 'react';
function UserProfile() {
const [user, setUser] = useState({
firstName: '',
lastName: '',
email: '',
});
const handleInputChange = (event) => {
const { name, value } = event.target;
setUser({ ...user, [name]: value });
};
// return part
}
return (
<div>
<input type="text" name="firstName" value={user.firstName}
onChange={handleInputChange} placeholder="First Name" />
<input type="text" name="lastName" value={user.lastName}
onChange={handleInputChange} placeholder="Last Name"/>
<input type="email" name="email" value={user.email}
onChange={handleInputChange} placeholder="Email" />
<p>{`Hello, ${user.firstName} ${user.lastName}`}</p>
<p>Email: {user.email}</p>
</div>
);
The useEffect
Hook in React is used to manage side effects in functional components. It allows you to perform tasks that can't or shouldn't happen within the regular rendering process. Common use cases include data fetching, DOM manipulation, and subscribing to external data sources.
useEffect(() => {
// Your side effect code here
return ()=>{
// clean up of the component
}
}, [dependencies]);
useEffect(() => {
// Your side effect code here
}, [dependencies]);
import React, { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
console.log('Component is mounted.');
return () => {
console.log('Component is unmounted.');
};
}, []); // An empty dependency array means this effect runs only on mount
return <div>My Component Content</div>;
}
export default MyComponent;
import React, { useEffect } from 'react';
function MyComponent() {
const [data,setData]=useState([])
useEffect(() => {
fetch('https://api.example.com/data')
.then((response) => response.json())
.then((data) => {
setData(data);
})
.catch((error) => {
// Handle errors
});
}, []);
return <div>{data}</div>;
}
export default MyComponent;
import React, { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
// Update the title of the document
document.title = `New Page Title`;
}, []);
return <div>My component</div>;
}
export default MyComponent;
// eventEmitter.js
import { EventEmitter } from 'events';
const eventEmitter = new EventEmitter();
export const subscribe = (event, callback) => {
eventEmitter.on(event, callback);
return () => {
eventEmitter.off(event, callback);
};
};
export const publish = (event, data) => {
eventEmitter.emit(event, data);
};
function Publisher() {
const [input, setInput] = useState('');
const handlePublish = () => {
publish('message', input);
setInput('');
};
return (
<div>
<h2>Publisher</h2>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
/>
<button onClick={handlePublish}>Publish</button>
</div>
);
}
function Subscriber() {
const [message, setMessage] = useState('');
useEffect(() => {
const unsubscribe = subscribe('message', (data) => {
setMessage(data);
});
// Cleanup subscription on unmount
return () => {
unsubscribe();
};
}, []);
return (
<div>
<h2>Subscriber</h2>
<p>Message: {message}</p>
</div>
);
}
React enforces strict rules when working with Hooks to ensure consistency and avoid common issues.
useReducer
is a hook that is used for managing state in React components, similar to useState
.useState`
.useReducer
?type
field and optionally other fields for payload.const [state, dispatch] = useReducer(reducer, initialState);
function counterReducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return { count: 0 };
default:
throw new Error(`Unknown action: ${action.type}`);
}
}
import React, { useReducer } from 'react';
function Counter() {
const initialState = { count: 0 };
const [state, dispatch] = useReducer(counterReducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</div>
);
}
export default Counter;
function todoReducer(state, action) {
switch (action.type) {
case 'add':
return [...state, { id: Date.now(), text: action.text, completed: false }];
case 'toggle':
return state.map(todo =>
todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
);
case 'remove':
return state.filter(todo => todo.id !== action.id);
default:
throw new Error(`Unknown action: ${action.type}`);
}
}
function TodoApp() {
const initialState = [];
const [state, dispatch] = useReducer(todoReducer, initialState);
const [text, setText] = useState('');
const handleAddTodo = () => {
dispatch({ type: 'add', text });
setText('');
};
return (
<div>
<h2>To-Do List</h2>
<input type="text" value={text} onChange={(e) => setText(e.target.value)} />
<button onClick={handleAddTodo}>Add Todo</button>
<ul>
{state.map(todo => (
<li key={todo.id} onClick={() => dispatch({ type: 'toggle', id: todo.id })}
style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
>
{todo.text}
<button onClick={() => dispatch({ type: 'remove', id: todo.id })}>
Remove
</button>
</li>
))}
</ul>
</div>
);
}
useMemo
is a React hook that memoizes the result of a computation, caching the value and recalculating it only when one of its dependencies changes.useMemo
?const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
computeExpensiveValue
: A function that performs an expensive computation.[a, b]
: An array of dependencies. The computation will only re-run if one of these dependencies' changes.const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape'];
function FilterComponent() {
const [filter, setFilter] = useState('');
const filteredItems = items.filter(item => item.toLowerCase().includes(filter.toLowerCase()));
return (
<div>
<input type="text" value={filter} onChange={(e) => setFilter(e.target.value)} placeholder="Filter items" />
<ul>
{filteredItems.map((item, index) => ( <li key={index}>{item}</li> ))}
</ul>
</div>
);
}
export default FilterComponent;
import React, { useState, useMemo } from 'react';
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape'];
function FilterComponent() {
const [filter, setFilter] = useState('');
const filteredItems = useMemo(() => {
return items.filter(item => item.toLowerCase().includes(filter.toLowerCase()));
}, [filter]);
return (
<div>
<input type="text" value={filter} onChange={(e) => setFilter(e.target.value)} placeholder="Filter items" />
<ul>
{filteredItems.map((item, index) => ( <li key={index}>{item}</li> ))}
</ul>
</div>
);
}
export default FilterComponent;
useCallback
is a React hook that returns a memoized version of a callback function.React.memo
).useMemo
?const memoizedCallback = useCallback(() => {
// Your callback function logic
}, [dependencies]);
function Counter() {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount((prevCount) => prevCount + 1);
}, []);
const reset = useCallback(() => {
setCount(0);
}, []);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={reset}>Reset</button>
</div>
);
}
Custom Hooks are functions that allow you to extract and reuse stateful logic across components.
use
prefix (e.g., useCustomHook
).import { useState } from 'react';
function useCounter(initialCount) {
const [count, setCount] = useState(initialCount);
const increment = () => {
setCount(count + 1);
};
return { count, increment };
}
export default useCounter;
import { useState, useEffect } from 'react';
function useDarkMode() {
const [isDarkMode, setIsDarkMode] = useState(false);
useEffect(() => {
// Check local storage for user preference
const storedValue = localStorage.getItem('darkMode');
if (storedValue) {
setIsDarkMode(JSON.parse(storedValue));
}
}, []);
useEffect(() => {
// Update the DOM and local storage when isDarkMode changes
localStorage.setItem('darkMode', JSON.stringify(isDarkMode));
document.body.classList.toggle('dark-mode', isDarkMode);
}, [isDarkMode]);
return [isDarkMode, setIsDarkMode];
}
export default useDarkMode;
By Youcef Madadi