React Concepts coding - rs-hash/Senior GitHub Wiki
Here's an example of a class component in React that includes state and lifecycle methods:
import React, { Component } from 'react';
class ClassComponent extends Component {
constructor(props) {
super(props);
// Initialize state
this.state = {
count: 0,
};
}
// Lifecycle method: componentDidMount
componentDidMount() {
console.log('Component is mounted.');
}
// Lifecycle method: componentDidUpdate
componentDidUpdate(prevProps, prevState) {
console.log('Component updated.');
console.log('Previous state:', prevState);
console.log('Current state:', this.state);
}
// Lifecycle method: componentWillUnmount
componentWillUnmount() {
console.log('Component will unmount.');
}
// Custom method to increment the count
incrementCount = () => {
this.setState((prevState) => ({
count: prevState.count + 1,
}));
};
render() {
return (
<div>
<h1>Class Component with State and Lifecycle Methods</h1>
<p>Count: {this.state.count}</p>
<button onClick={this.incrementCount}>Increment</button>
</div>
);
}
}
export default ClassComponent;
In this class component:
-
We create a class
ClassComponent
that extendsComponent
, the base class for class components in React. -
In the constructor, we initialize the component's state with an initial count of 0.
-
We implement several lifecycle methods:
-
componentDidMount
: Called after the component has been mounted (i.e., added to the DOM). -
componentDidUpdate
: Called after the component has been updated (i.e., after a state change or prop change). -
componentWillUnmount
: Called before the component is unmounted (i.e., removed from the DOM).
-
-
We define a custom method
incrementCount
that updates thecount
state when a button is clicked. -
In the
render
method, we display the current count and a button to increment it.
This class component demonstrates the use of state, lifecycle methods, and a custom method to manage and display state within a React component.
Props and State in React:
Props and state are fundamental concepts in React for managing data and communication between components. They serve different purposes:
-
Props (Properties):
-
Props are read-only: They are used to pass data from parent components (or the parent component's state) to child components. Props are immutable, meaning they cannot be modified by the child component that receives them.
-
Props are external: They allow data to flow from top-down (from parent to child). They help in making components reusable and composable since child components can receive data from their parent components.
-
Props are used for configuration: You pass props to a component to configure its behavior or to provide data that it needs to display.
-
-
State:
-
State is mutable: State is used to manage and store data that can change over time within a component. Unlike props, components can modify their own state using the
setState
method. -
State is internal: It is local to the component where it is defined. Other components cannot directly access or modify another component's state.
-
State is used for interactivity: You use state to manage the internal state of a component. For example, you might use state to track form input, toggle a menu, or manage a counter.
-
Building a Component that Receives Props and Updates State: Certainly! Here's an example of a functional component in React that receives props and updates its state based on user interactions:
import React, { useState } from 'react';
function FunctionalComponent({ someProp }) {
// Initialize state
const [count, setCount] = useState(0);
// Custom function to increment the count in state
const incrementCount = () => {
setCount(count + 1);
};
return (
<div>
<h1>FunctionalComponent</h1>
<p>Received Prop: {someProp}</p>
<p>Current Count: {count}</p>
<button onClick={incrementCount}>Increment Count</button>
</div>
);
}
export default FunctionalComponent;
Implement parent-child communication using props.
In React, parent-child communication using props is a fundamental way to pass data from a parent component to a child component. Here's an example of how you can achieve parent-child communication using props in functional components:
ParentComponent.js:
import React, { useState } from 'react';
import ChildComponent from './ChildComponent';
function ParentComponent() {
const [message, setMessage] = useState('Hello from Parent!');
const handleMessageChange = () => {
setMessage('Updated message from Parent!');
};
return (
<div>
<h1>Parent Component</h1>
<p>Message: {message}</p>
<button onClick={handleMessageChange}>Change Message</button>
<ChildComponent message={message} />
</div>
);
}
export default ParentComponent;
ChildComponent.js:
import React from 'react';
function ChildComponent({ message }) {
return (
<div>
<h2>Child Component</h2>
<p>Received Message: {message}</p>
</div>
);
}
export default ChildComponent;
Convert a class component to a functional component using hooks.
Sure, let's convert a class component to a functional component using hooks. Here's an example:
Class Component (Before Conversion):
import React, { Component } from 'react';
class CounterClassComponent extends Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
}
incrementCount = () => {
this.setState((prevState) => ({
count: prevState.count + 1,
}));
};
render() {
return (
<div>
<h1>Counter (Class Component)</h1>
<p>Count: {this.state.count}</p>
<button onClick={this.incrementCount}>Increment</button>
</div>
);
}
}
export default CounterClassComponent;
Functional Component with Hooks (After Conversion):
import React, { useState } from 'react';
function CounterFunctionalComponent() {
// Initialize state using the useState hook
const [count, setCount] = useState(0);
// Define a function to increment the count
const incrementCount = () => {
setCount((prevCount) => prevCount + 1);
};
return (
<div>
<h1>Counter (Functional Component with Hooks)</h1>
<p>Count: {count}</p>
<button onClick={incrementCount}>Increment</button>
</div>
);
}
export default CounterFunctionalComponent;
You can conditionally render a functional component in React based on a boolean value or user authentication by using a simple JavaScript conditional statement within your JSX. Here's an example:
import React from 'react';
function AuthenticatedComponent() {
return <p>Welcome, authenticated user!</p>;
}
function UnauthenticatedComponent() {
return <p>Please log in to access this content.</p>;
}
function App() {
const isAuthenticated = true; // Replace with your authentication logic
return (
<div>
<h1>Conditional Rendering Example</h1>
{isAuthenticated ? <AuthenticatedComponent /> : <UnauthenticatedComponent />}
</div>
);
}
export default App;
You can render a list of items using the map
function and unique keys in a functional component in React. To add delete functionality to remove items from the list, you can create a delete button for each item and implement an event handler to handle the item removal. Here's an example:
import React, { useState } from 'react';
function ItemList() {
const [items, setItems] = useState([
{ id: 1, text: 'Item 1' },
{ id: 2, text: 'Item 2' },
{ id: 3, text: 'Item 3' },
]);
const handleDelete = (itemId) => {
// Filter out the item with the given itemId
const updatedItems = items.filter((item) => item.id !== itemId);
setItems(updatedItems);
};
return (
<div>
<h1>Item List</h1>
<ul>
{items.map((item) => (
<li key={item.id}>
{item.text}
<button onClick={() => handleDelete(item.id)}>Delete</button>
</li>
))}
</ul>
</div>
);
}
export default ItemList;
You can create a controlled functional form component in React that updates its state as the user types by using the useState
hook to manage the form input's value and the onChange
event handler to update the state. Here's an example:
import React, { useState } from 'react';
function ControlledForm() {
// Initialize state for the form input
const [formData, setFormData] = useState({
firstName: '',
lastName: '',
});
// Event handler to update form data
const handleInputChange = (event) => {
const { name, value } = event.target;
setFormData({
...formData,
[name]: value,
});
};
return (
<div>
<h1>Controlled Form</h1>
<form>
<div>
<label htmlFor="firstName">First Name:</label>
<input
type="text"
id="firstName"
name="firstName"
value={formData.firstName}
onChange={handleInputChange}
/>
</div>
<div>
<label htmlFor="lastName">Last Name:</label>
<input
type="text"
id="lastName"
name="lastName"
value={formData.lastName}
onChange={handleInputChange}
/>
</div>
</form>
<div>
<p>First Name: {formData.firstName}</p>
<p>Last Name: {formData.lastName}</p>
</div>
</div>
);
}
export default ControlledForm;
To validate form input in a functional component and display error messages, you can add validation logic to your handleInputChange
function and conditionally render error messages based on the validation results. Here's an example of how to do that:
import React, { useState } from 'react';
function ControlledFormWithValidation() {
// Initialize state for the form input and error messages
const [formData, setFormData] = useState({
firstName: '',
lastName: '',
});
const [errors, setErrors] = useState({
firstName: '',
lastName: '',
});
// Event handler to update form data and validate
const handleInputChange = (event) => {
const { name, value } = event.target;
setFormData({
...formData,
[name]: value,
});
// Validation logic (you can customize this as needed)
if (name === 'firstName') {
if (value.length < 2) {
setErrors({
...errors,
firstName: 'First name must be at least 2 characters long.',
});
} else {
setErrors({
...errors,
firstName: '',
});
}
} else if (name === 'lastName') {
if (value.length < 2) {
setErrors({
...errors,
lastName: 'Last name must be at least 2 characters long.',
});
} else {
setErrors({
...errors,
lastName: '',
});
}
}
};
return (
<div>
<h1>Controlled Form with Validation</h1>
<form>
<div>
<label htmlFor="firstName">First Name:</label>
<input
type="text"
id="firstName"
name="firstName"
value={formData.firstName}
onChange={handleInputChange}
/>
{errors.firstName && <p className="error">{errors.firstName}</p>}
</div>
<div>
<label htmlFor="lastName">Last Name:</label>
<input
type="text"
id="lastName"
name="lastName"
value={formData.lastName}
onChange={handleInputChange}
/>
{errors.lastName && <p className="error">{errors.lastName}</p>}
</div>
</form>
</div>
);
}
export default ControlledFormWithValidation;
In React, you can handle various events like clicks, input changes, and form submissions by defining event handlers. Here are examples for each of these event types:
-
Click Events:
To handle click events, you can use the
onClick
attribute on an HTML element like a button or a link. Here's an example of handling a click event:import React from 'react'; function ClickEventExample() { const handleClick = () => { alert('Button clicked!'); }; return ( <div> <h1>Click Event Example</h1> <button onClick={handleClick}>Click me</button> </div> ); } export default ClickEventExample;
In this example, when the "Click me" button is clicked, the
handleClick
function is executed, and an alert is shown. -
Input Change Events:
To handle input changes, you can use the
onChange
attribute on an input element. Here's an example:import React, { useState } from 'react'; function InputChangeExample() { const [inputValue, setInputValue] = useState(''); const handleInputChange = (event) => { setInputValue(event.target.value); }; return ( <div> <h1>Input Change Event Example</h1> <input type="text" value={inputValue} onChange={handleInputChange} placeholder="Type something..." /> <p>You typed: {inputValue}</p> </div> ); } export default InputChangeExample;
In this example, as the user types into the input field, the
handleInputChange
function updates theinputValue
state, and the typed text is displayed. -
Form Submission Events:
To handle form submissions, you can use the
onSubmit
attribute on a<form>
element. Here's an example:import React, { useState } from 'react'; function FormSubmitExample() { const [formData, setFormData] = useState({ username: '', email: '', }); const handleSubmit = (event) => { event.preventDefault(); // Perform form submission logic (e.g., send data to the server) console.log('Form submitted with data:', formData); }; const handleInputChange = (event) => { const { name, value } = event.target; setFormData({ ...formData, [name]: value, }); }; return ( <div> <h1>Form Submission Event Example</h1> <form onSubmit={handleSubmit}> <label htmlFor="username">Username:</label> <input type="text" id="username" name="username" value={formData.username} onChange={handleInputChange} /> <label htmlFor="email">Email:</label> <input type="email" id="email" name="email" value={formData.email} onChange={handleInputChange} /> <button type="submit">Submit</button> </form> </div> ); } export default FormSubmitExample;
In this example, when the form is submitted, the
handleSubmit
function is called, and we prevent the default form submission behavior usingevent.preventDefault()
. The form data is then logged to the console, but you can replace that with your submission logic.
These examples demonstrate how to handle click events, input change events, and form submission events in React components. You can customize the event handlers to perform specific actions based on your application's requirements.
Event delegation and event bubbling are concepts that are not specific to React but are part of the DOM event handling mechanism. React utilizes these concepts as well because it's built on top of the DOM. Let's look at how event delegation and event bubbling work in a React context.
Event Bubbling:
Event bubbling is a mechanism in which an event triggered on a nested DOM element will propagate up the DOM tree, triggering the same event on its ancestors. In React, you can handle bubbling events by defining event handlers on parent components and allowing the events to propagate up to them. Here's an example:
import React from 'react';
function ParentComponent() {
const handleParentClick = () => {
alert('Parent Component Clicked');
};
return (
<div onClick={handleParentClick} style={{ border: '1px solid #ccc', padding: '20px' }}>
<h1>Parent Component</h1>
<ChildComponent />
</div>
);
}
function ChildComponent() {
const handleChildClick = () => {
alert('Child Component Clicked');
};
return (
<div onClick={handleChildClick} style={{ border: '1px solid #aaa', padding: '10px' }}>
<h2>Child Component</h2>
<button>Click Me (Child)</button>
</div>
);
}
export default ParentComponent;
Event Delegation:
Event delegation is a technique where you attach a single event listener to a common ancestor element of multiple child elements instead of attaching individual event listeners to each child element. In React, this can be achieved by handling events in a parent component and using the event.target
property to determine which child element was the actual target of the event. Here's an example:
import React from 'react';
function ParentComponent() {
const handleButtonClick = (event) => {
if (event.target.tagName === 'BUTTON') {
alert('Button clicked: ' + event.target.textContent);
}
};
return (
<div onClick={handleButtonClick}>
<h1>Parent Component</h1>
<button>Click Me 1</button>
<button>Click Me 2</button>
</div>
);
}
export default ParentComponent;
Create a higher-order component for reusable logic. Explain the benefits and use cases of HOCs.
Higher-Order Component (HOC) is a design pattern in React that allows you to reuse component logic. It's not a component itself but rather a function that takes a component and returns a new component with enhanced functionality. HOCs are a way to share behavior between components without duplicating code.
Here's a simple example of a Higher-Order Functional Component for reusable logic:
Sure, here's a simple example of implementing user authentication logic using a functional Higher-Order Component (HOC) in React:
import React, { useState } from 'react';
// Simulated authentication logic
const fakeAuthService = {
isAuthenticated: false,
login: () => {
fakeAuthService.isAuthenticated = true;
},
logout: () => {
fakeAuthService.isAuthenticated = false;
},
};
// Define a Higher-Order Component (HOC) function
const withAuth = (WrappedComponent) => {
// This HOC adds authentication-related props to the wrapped component
return function WithAuth(props) {
const [authenticated, setAuthenticated] = useState(fakeAuthService.isAuthenticated);
const login = () => {
fakeAuthService.login();
setAuthenticated(true);
};
const logout = () => {
fakeAuthService.logout();
setAuthenticated(false);
};
// Pass authentication-related props to the wrapped component
return (
<WrappedComponent
isAuthenticated={authenticated}
login={login}
logout={logout}
{...props}
/>
);
};
};
// Create a component that requires authentication
const AuthenticatedComponent = ({ isAuthenticated, login, logout }) => {
return (
<div>
<p>Welcome to the authenticated component!</p>
<p>Authenticated: {isAuthenticated ? 'Yes' : 'No'}</p>
{isAuthenticated ? (
<button onClick={logout}>Logout</button>
) : (
<button onClick={login}>Login</button>
)}
</div>
);
};
// Wrap AuthenticatedComponent with the withAuth HOC
const AuthComponentWithAuth = withAuth(AuthenticatedComponent);
// App component
const App = () => {
return (
<div>
<h1>Authentication Example</h1>
<AuthComponentWithAuth />
</div>
);
};
export default App;
In this example:
-
We have a simulated authentication service (
fakeAuthService
) withlogin
andlogout
functions. -
The
withAuth
HOC takes aWrappedComponent
and adds authentication-related props (isAuthenticated
,login
, andlogout
) to it. -
The
AuthenticatedComponent
displays the user's authentication status and provides buttons for login and logout. The behavior of the buttons depends on the authentication status. -
We wrap the
AuthenticatedComponent
with thewithAuth
HOC to createAuthComponentWithAuth
. -
In the
App
component, we renderAuthComponentWithAuth
to demonstrate how components wrapped with thewithAuth
HOC can access authentication-related props.
When you run this code, you'll see a simple authentication example where the user can log in and out, and the authentication status is passed as a prop to the wrapped component.
// Define an HOC function
const withColor = (WrappedComponent, color) => {
// This HOC adds a color prop to the wrapped component
return (props) => <WrappedComponent {...props} color={color} />;
};
// Create a functional component to be wrapped
const TextComponent = ({ text, color }) => (
<div style={{ color }}>{text}</div>
);
// Wrap TextComponent with the withColor HOC
const TextWithColor = withColor(TextComponent, "blue");
<TextWithColor text="Colored text"></TextWithColor>
Implement error boundaries to catch and handle errors in React components. Discuss best practices for error handling.
Error boundaries in React can also be implemented in functional components using the useEffect
and useState
hooks. In this example, we'll create a functional error boundary and discuss best practices for error handling.
Implementing Error Boundaries in Functional Components:
import React, { useState, useEffect } from 'react';
// Functional error boundary component
const ErrorBoundary = ({ children }) => {
const [hasError, setHasError] = useState(false);
useEffect(() => {
const errorHandler = (error) => {
console.error(error);
setHasError(true);
};
// Add a global error handler for uncaught errors
window.addEventListener('error', errorHandler);
// Clean up the event listener
return () => {
window.removeEventListener('error', errorHandler);
};
}, []);
if (hasError) {
// You can render a fallback UI when an error occurs
return <div>Something went wrong.</div>;
}
return children;
};
// Example component that may throw an error
const MyComponent = () => {
const throwError = () => {
throw new Error('An error occurred');
};
return (
<div>
<button onClick={throwError}>Throw Error</button>
</div>
);
};
// App component
const App = () => {
return (
<div>
<h1>Error Boundary Example</h1>
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
</div>
);
};
export default App;
Implement code-splitting and lazy loading of components. Demonstrate how Suspense works with React.lazy and ErrorBoundary.
Code splitting and lazy loading of components in React allow you to load parts of your application only when they are needed, improving initial load times and reducing the size of the bundle. Additionally, React's Suspense
component can be used with React.lazy
and ErrorBoundary
to handle loading and errors gracefully.
Here's how to implement code splitting, lazy loading, and demonstrate Suspense
with React.lazy
and ErrorBoundary
in React functional components:
-
Create a Dynamic Import
First, create a dynamic import for the component you want to code split and lazy load. Let's assume we have a component called
LazyComponent
:// LazyComponent.js import React from 'react'; const LazyComponent = () => { return <div>Lazy Component</div>; }; export default LazyComponent;
-
Lazy Load the Component
Use
React.lazy
to dynamically load the component when needed. Wrap it with anErrorBoundary
to catch and handle any errors:// App.js import React, { Suspense } from 'react'; // Define an ErrorBoundary component const ErrorBoundary = ({ children }) => { // Error handling logic here return children; }; // Lazy load the component with React.lazy const LazyComponent = React.lazy(() => import('./LazyComponent')); const App = () => { return ( <div> <h1>Lazy Loading and Suspense Example</h1> <ErrorBoundary> <Suspense fallback={<div>Loading...</div>}> <LazyComponent /> </Suspense> </ErrorBoundary> </div> ); }; export default App;
In the above code, we use
React.lazy(() => import('./LazyComponent'))
to loadLazyComponent
lazily when it's needed. TheSuspense
component wrapsLazyComponent
and provides a fallback UI (in this case, "Loading...") while the component is being loaded.
To implement a lazy-loaded component in routing in a React application, you can use React's React.lazy
along with React Router. Lazy loading components in routing can significantly improve the initial load time of your application by loading components only when they are needed.
-
Error Handling with ErrorBoundary
Define an
ErrorBoundary
component to catch and handle errors. You can implement your error handling logic within this component:const ErrorBoundary = ({ children }) => { const [hasError, setHasError] = React.useState(false); React.useEffect(() => { // Simulate an error for demonstration purposes if (Math.random() > 0.5) { setHasError(true); } }, []); if (hasError) { return <div>Something went wrong.</div>; } return children; };
// App.js or your routing file
import React, { lazy, Suspense } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
// Create a dynamic import for the LazyComponent
const LazyComponent = lazy(() => import('./LazyComponent'));
const LoadingFallback = () => <div>Loading...</div>;
const App = () => {
return (
<Router>
<Switch>
<Route exact path="/" component={Home} />
<Route
path="/lazy"
render={() => (
<Suspense fallback={LoadingFallback}>
<LazyComponent />
</Suspense>
)}
/>
{/* Other routes */}
</Switch>
</Router>
);
};
export default App;
Style components using CSS-in-JS libraries like styled-components or Emotion. Discuss the benefits of styled-components.
Styled-components and Emotion are popular CSS-in-JS libraries that allow you to style React components using JavaScript. These libraries provide a way to write and manage CSS directly within your JavaScript code. Below, I'll demonstrate how to style components using styled-components in a React functional component and discuss the benefits of using styled-components.
Using styled-components in a React Functional Component:
First, make sure you have styled-components
installed in your project:
npm install styled-components
Here's an example of how to use styled-components
in a React functional component:
import React from 'react';
import styled from 'styled-components';
// Create a styled component
const StyledButton = styled.button`
background-color: #007bff;
color: white;
padding: 10px 20px;
border: none;
cursor: pointer;
transition: background-color 0.3s;
&:hover {
background-color: #0056b3;
}
`;
const App = () => {
return (
<div>
<h1>Styled Components Example</h1>
<StyledButton>Click me</StyledButton>
</div>
);
};
export default App;
You can use async/await
syntax to make your code cleaner and easier to read when dealing with asynchronous operations like HTTP requests using fetch
and Axios. Here's how you can rewrite the previous code snippets using async/await
:
Using async/await
with Fetch API:
// GET Request with Fetch and async/await
async function fetchPost() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
console.log('GET Request with Fetch and async/await:', data);
} catch (error) {
console.error('Error:', error);
}
}
// POST Request with Fetch and async/await
async function postNewData() {
const postData = {
title: 'foo',
body: 'bar',
userId: 1
};
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(postData)
});
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
console.log('POST Request with Fetch and async/await:', data);
} catch (error) {
console.error('Error:', error);
}
}
// Call the functions
fetchPost();
postNewData();
Using async/await
with Axios:
const axios = require('axios'); // Node.js environment
// GET Request with Axios and async/await
async function axiosGet() {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/posts/1');
console.log('GET Request with Axios and async/await:', response.data);
} catch (error) {
console.error('Error:', error);
}
}
// POST Request with Axios and async/await
async function axiosPost() {
const postData = {
title: 'foo',
body: 'bar',
userId: 1
};
try {
const response = await axios.post('https://jsonplaceholder.typicode.com/posts', postData);
console.log('POST Request with Axios and async/await:', response.data);
} catch (error) {
console.error('Error:', error);
}
}
// Call the functions
axiosGet();
axiosPost();
In these examples, we use async
functions (fetchPost
, postNewData
, axiosGet
, axiosPost
) to encapsulate the asynchronous operations, and we await
the results of those operations. This makes the code more readable and easier to understand. Error handling is also done in a more structured way using try/catch
blocks.