In the root of the ASP.NET Core web project, create the following folder structure:
ClientApp/ src/ index.js components/
We use the ClientApp folder and structure to follow common naming conventions.
Open a new command prompt window and navigate to the ClientApp folder.
The package.json file is used in Nodejs projects to store general information about the project (like its name, version, etc) and track what dependencies the project needs (so that anyone can install them all at once).
To create a “default” package.json file, type the “npm init” command with the y option:
npm init -y
The “-y” option uses default options.
Once the npm init
command is done, you should have a package.json
file under your project directory (and nothing else, yet).
The package.json
file can now be used to "document" any dependencies you add to your project. This happens automatically when you npm install
anything.
If the command was successful, it should have created a package.json file with contents similar to this:
{ "name": "demo", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }
Since you’ll be writing your code in multiple modules (files) and it will depend on other modules (like React), you need a module bundler to translate all these modules into something that can work in all browsers today. You can use Webpack for that job.
To install the packages you need for Webpack (which is also a “module”), type the following command:
npm i -D webpack webpack-cli
If the command ran successfully, it should have created a new folder named “node_modules” in your project folder and downloaded several modules into this folder (into various folders), of course including the Webpack package as well.
Instead of a run-time dependency, the “ — save-dev” option adds a “dev dependency” to the package.json file. This indicates that the Webpack package is only useful during development. That would make sense, as once we have created the “bundles” as we require, we would use them directly in production. The package.json file should look similar to this:
{
// ...
},
"devDependncies": {
"webpack": "^5.69.1",
"webpack-cli": "^4.9.2"
}
}
Official website: webpack bundle your styles
Babel is a transpiler. The transpiler transforms ES6 code to use ES5 syntax that older browsers can understand. While Babel itself is very feature-rich and can be used independently, for our purposes, we can use the corresponding babel “loaders” to do the job.
Webpack is just a generic module bundler. You need to configure it with loaders to transform code from one state into the other.
Before we can use any loaders, we need first to install them. For our purpose, we will have to install the relevant babel loaders. Use the following command to install the babel loaders and the “presets”.
npm i -D babel-loader @babel/core @babel/preset-env @babel/preset-react
If the command ran successfully, these loaders would be installed in the node_modules folder and the package.json would be modified.
A priceless development dependency is ESLint.
ESLint is a code quality tool and if you don’t use it, your code will not be as good as it could be.
Since Babel is part of this stack, you need to configure ESLint to parse through what Babel is going to parse through. You should also use the main recommended ESLint configurations in addition to those recommended for React projects. Here are the packages you need for that:
npm i -D eslint @babel/eslint-parser eslint-plugin-react eslint-plugin-react-hooks
Updating babel-eslint to @babel/eslint-parser for React apps | Tim Addison (tjaddison.com)
The most popular testing library that’s usually used with React is Jest. Install that if you plan to write tests for your React project (and you should!). You’ll also need babel-jest
and a test renderer like react-test-renderer
:
npm i -D jest babel-jest react-test-renderer
The frontend dependencies you need are React and ReactDOM. Install them next:
npm i -D react react-dom
Install the Babel preset to process React JSX code:
npm i -D @babel/preset-react
To configure Webpack to bundle your application into a single bundle file, create a webpack.config.js
file under the root of the project and put the following module.exports
object in it:
webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/index.jsx',
output: {
path: path.resolve(__dirname, '../wwwroot/js'),
filename: 'snippset.js'
},
module: {
rules: [
{
test: /\.jsx$/,
exclude: /(node_modules)/,
use: [{
loader: 'babel-loader',
}
]
}
]
},
resolve: {
extensions: ['.js', '.jsx']
}
};
Webpack has certain defaults on which JavaScript file to start with. It looks for asrc/index.js
file. It’ll also output the bundle todist/main.js
by default. If you need to change the locations of yoursrc
anddist
files, you’ll need a few more configuration entries inwebpack.config.js
.
To configure Babel to compile JSX and modern JavaScript code, create a babel.config.js
file under the root of the project and put the following module.exports
object in it:
babel.config.js
module.exports = {
presets: ['@babel/preset-react', '@babel/preset-env'],
};
These presets were already installed above. The env preset is the one Babel uses to transform modern JavaScript (and it’s configurable if you need to target just modern browsers and make the bundle smaller). The react preset is for the mighty JSX extension.
To configure ESLint, you need to add a .eslintrc.js
file in the root of the project. This file will naturally depend on your code style preferences, but definitely start it with the recommended configurations and then customize them as needed:
.eslintrc.js
module.exports = { parser: 'babel-eslint', env: { browser: true, commonjs: true, es6: true, node: true, jest: true, }, parserOptions: { ecmaVersion: 2020, ecmaFeatures: { impliedStrict: true, jsx: true, }, sourceType: 'module', }, plugins: ['react', 'react-hooks'], extends: [ 'eslint:recommended', 'plugin:react/recommended', 'plugin:react-hooks/recommended', ], settings: { react: { version: 'detect', }, }, rules: { // You can do your customizations here... // For example, if you don't want to use the prop-types package, // you can turn off that recommended rule with: 'react/prop-types': ['off'] }, };
You should make your editor highlight any ESLint issues for you on save! All the major editors today have plugins to do that. You should also make your editor auto-format code for you on save as well using Prettier. Prettier works great with ESLint.
Install the Terser plugin
terser-webpack-plugin
webpack-contrib/terser-webpack-plugin: Terser Plugin (github.com)
In your package.json
file you should have a scripts
section. If you generated the file with the npm init
defaults you’ll have a placeholder "test" script in there. You should change that to work with Jest.
Add more scripts in there to build the scripts with runner for Webpack.
{
// ...
"scripts": {
"build:dev": "webpack --stats verbose",
"build:prd": "webpack build --config ./webpack.config.prd.js --stats verbose",
"build:watch": "webpack --watch",
"test": "jest"
},
The --mode=development
flag is to make Webpack generate a development-friendly bundle. Run Webpack with --mode=production
in production.
The --watch
flag in the command above is to run Webpack in watch mode
webpack.config.prd.js
const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
mode: 'production',
entry: './src/index.jsx',
output: {
path: path.resolve(__dirname, '../wwwroot/dist/js'),
filename: 'snippset.min.js'
},
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
format: {
comments: false,
},
},
extractComments: false,
}),
],
},
module: {
rules: [
{
test: /\.jsx$/,
exclude: /(node_modules)/,
use: [{
loader: 'babel-loader',
}
]
}
]
},
resolve: {
extensions: ['.js', '.jsx']
}
};
Remove Comments
Resources:
JSX supports expression in pure JavaScript syntax. The expression has to be enclosed inside the curly braces, { }. The expression can contain all variables available in the context where the JSX is defined. Let us create simple JSX with expression.
Example
<script type="text/babel">
var currentTime = new Date().toTimeString();
ReactDOM.render(
<div><p>The current time is {currentTime}</p></div>,
document.getElementById('app') );
</script>
Output
Here, cTime is used in the JSX using expression. The output of the above code is as follows,
15:57:48 GMT-0700 (Pacific Daylight Time)
One of the positive side effects of using an expression in JSX is that it prevents Injection attacks as it converts any string into an HTML-safe string.
JSX supports user-defined JavaScript functions. Function usage is similar to expression. Let us create a simple function and use it inside JSX.
Example
<script type="text/babel">
var currentTime = new Date().toTimeString();
ReactDOM.render(
<div><p>The current time is {currentTime}</p></div>,
document.getElementById('app')
);
</script>
Output
Here, getCurrentTime()
is used to get the current time, and the output is similar as specified below −
16:37:32 GMT-0700 (Pacific Daylight Time)
TanStack Query is a data-fetching library for React applications. It simplifies communication with APIs by abstracting complex logic into simple queries. With TanStack Query, you can define queries using plain JavaScript objects, leverage caching and automatic refetching, handle pagination, and easily manage loading and error states. It enhances productivity and improves the data-fetching experience in React projects.
To use useState, you must first import it from the React library. Then you can call the useState function with an initial value for your state variable. This will return an array with two elements: the current value of the state variable and a function that you can use to update that value.
For example, let's say you want to create a counter that increments each time a button is clicked. You could use useState to create a state variable for the count like this:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
return (
<div>
<p>You clicked {count} times</p>
<button onClick={handleClick}>Click me</button>
</div>
);
}
In this example, we're using the useState hook to create a state variable called count
, which is initially set to 0. We're also defining a function called handleClick
that updates the count variable by calling the setCount
function with the new value of count + 1
. Finally, we're rendering the current value of count
in a paragraph tag, along with a button that calls the handleClick
function when clicked.
useContext
is a React Hook that lets you read and subscribe to context from your component. React Context is a way to manage state globally.
Event handlers determine what action is to be taken whenever an event is fired. This could be a button click or a change in a text input.
You would write this as follows in React:
function ActionLink() {
const handleClick = (e) => {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<button onClick={handleClick}>Click me</button>
);
}
Inline functions allow you to write code for event handling directly in JSX. See the example below:
import React from "react";
const App = () => {
return (
<button onClick={() => alert("Hello!")}>Say Hello</button>
);
};
export default App;
Another common use case for event handlers is passing a parameter to a function in React so it can be used later. For example:
import React from "react";
const App = () => {
const sayHello = (name) => {alert(`Hello, ${name}!`);
};
return (
<button onClick={() => {sayHello("John");}}>Say Hello</button>
);
};
export default App;
React hooks are functions that let you use state and other React features in functional components. They provide an easier way to manage component state, handle side effects, and access lifecycle methods. Hooks like useState and useEffect help you write cleaner and more reusable code, making React development easier and more intuitive.
React Router is a popular routing library for building single-page web applications with React, a JavaScript library for building user interfaces. Routing in web applications is the process of determining which components or views to display based on the URL or user navigation. React Router provides a way to handle routing in React applications by allowing you to map different URLs to specific components, enabling the creation of multi-page experiences within a single-page application (SPA).
The useNavigate
hook from React Router returns a function that lets you navigate programmatically, for example in an effect:
import { useNavigate } from "react-router-dom";
function useLogoutTimer() {
const userIsInactive = useFakeInactiveUser();
const navigate = useNavigate();
useEffect(() => {
if (userIsInactive) {
fake.logout();
navigate("/session-timed-out");
}
}, [userIsInactive]);
}
Button click
import { useNavigate } from 'react-router-dom';
...
const navigate = useNavigate();
...
<Button onClick={() => navigate('../user', { replace: true })}>Register</Button>
Reference
If you use the same query in multiple places, consider adding a wrapper hook to keep your code clean:
function useAllBooks() {
return useQuery("books", getAllBooks)
}
function Component1() {
const {...} = useAllBooks()
return (...)
}
function Component2() {
const {...} = useAllBooks()
return (...)
}
This useArticleQuery
hook is designed to handle data fetching for articles based on the provided articleId
using the TanStack Query useQuery
function.
export const useArticleQuery = (articleId) => {
return useQuery({
queryKey: ["articles", articleId],
queryFn: () => getArticles(articleId)
});
};
The code snippet defines a custom hook called useArticleQuery
using the export
keyword, indicating it can be imported and used in other modules.
Inside the hook, the useQuery
function is called with an object as its argument. The object has two properties:
queryKey
: It is an array containing two elements: "articles" and articleId
. This array is used as a key to identify the query.
queryFn
: It is a function that gets executed to fetch the articles based on the given articleId
. The specific implementation of getArticles
is not shown in the provided code snippet.
In React, you can conditionally render components.
There are several ways to achieve conditional rendering:
If statement: Use a regular if
statement to conditionally render components or elements based on a condition.
Ternary operator: Use a ternary operator (condition ? trueBlock : falseBlock
) to render different components or elements based on a condition.
Logical && operator (Short Circuit Evaluation): Use the logical &&
operator (condition && renderBlock
) to conditionally render a block of JSX based on a condition. The block is only rendered if the condition is true.
Switch statement: Use a switch
statement to handle multiple conditions and render different components or elements based on each condition.
Inline condition: Use inline conditions within JSX ({condition ? renderBlock : null}
) to render a block of JSX based on a condition.
Render function: Use a render function or a component that returns JSX. Inside the function, you can implement any JavaScript logic to conditionally render components or elements.
The conditional (ternary) operator is the only JavaScript operator that takes three operands. This operator is frequently used as a shortcut for the if statement.
Syntax: {cond ? <A /> : <B />}
means “if cond
, render <A />
, otherwise <B />
”.
Sample
{itemCollapsed ? (
<CommandItem title='Expand' />
) : (
<CommandItem title='Collapse' />
)}
In React it often comes up when you want to render some JSX when the condition is true, or render nothing otherwise.
Syntax: {cond && <A />}
means “if cond
, render <A />
, otherwise nothing”.
Sample
{article.content.length > 0 && (
<ArticleContent content={article.content} slug={article.slug} />
)
What's the best way to get the data of a promise response of a useQuery() dependant query?
Option 1: you can check if data is available via isSuccess
:
const { isSuccess, data } = useQuery(
['articles', articleId],
getArticleById,
)
if (isSuccess) {
return data.map(...) // <-- data is guaranteed to be defined
}
Option 2: you can simply check if data exists, or use optional chaining:
const { isSuccess, data } = useQuery(
['articles', articleId],
getArticleById,
)
return data?.map(...) ?? null // <-- make sure to handle fallback with ??
You can assign a new value to clear or reset a useRef in your React app. For example, if you have a useRef called myRef, you can reset it by setting it to null or any other value you want. Here's an example:
import React, { useRef } from 'react';
function MyComponent() {
const myRef = useRef(null);
function handleClick() {
myRef.current = null; // reset the ref
}
return (
<div>
<button onClick={handleClick}>Reset Ref</button>
</div>
);
}
In this example, we create a useRef called myRef and initialize it to null. Then, we define a handleClick function that sets myRef.current to null when the button is clicked. This effectively resets the ref.
Note: Resetting a ref may not always be necessary or desirable, and you should only do it if it makes sense for your specific use case.
In this Snipp we show how to create a loading spinner component in React JS using pure CSS.
1. Create the React component to display the spinner
// Spinner.jsx
import React from "react";
import "./spinner.css";
export default function Spinner() {
return (
<div className='container'>
<div className='loader'></div>
</div>
);
}
2. Add CSS styles for loading spinner animation
/* spinner.css */
.container {
display: grid;
justify-content: center;
align-items: center;
}
.loader {
border: 3px solid #f3f3f3;
border-top: 3px solid #3498db;
border-radius: 50%;
width: 25px;
height: 25px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
Additional Resources:
Question: For me, if I invalidate the query, only the last page gets refetched and rendered. I want to fetch all of the pages
Solution: queryCache.clear()
does the trick for me
Additional resources:
Sometimes is helpful to know the scroll height of the window. Let's look at an example.
Suppose we have a component with two elements: a navigator and a main component.
import React from 'react';
import Nav from './Nav';
import Main from './Main';
export default const Page = () => {
return (
<div className = 'Page'>
<Nav/>
<Main/>
</div>
);
}
Let's suppose that Page.js
is a flex-box, Nav.js
is a sticky element, and Main.js
has a position: absolute
element inside of it.
If the element with absolute position is larger than the size of main, the sticky navigator will not work as expected (it won't stick).
We'll need to resize the Main.js
component and make it as big as the scroll height of the window. To do so, we need to read scrollHeight
property.
import React, { useEffect, useState } from 'react';
export default const Main = () => {
const [height, setHeight] = useState(0);
useEffect( () => { setHeight(document.documentElement.scrollHeight) });
return (
<div className = 'Main' style = {{height: `${height}px`}}>
<div className = 'Absolute'>This is an absolute positioned element.</div>
</div>
);
}
Now the main component will expand and the sticky element will work as expected.
React Context is a feature in the React library for JavaScript that allows you to manage and share state data, preferences, or any other data across your component tree without manually passing props through each component hierarchy level. It's particularly useful for building large applications where data needs to be accessible to multiple components at different levels in the component tree.
Key concepts and components related to React Context include:
createContext
: You start by creating a context object using the React.createContext()
function. This object consists of two components: Provider
and Consumer
.
Provider
: The Provider
component is used to wrap a part of your component tree. It accepts a value
prop that can be any JavaScript value (e.g., an object, a function, or a primitive). This value is then made accessible to all descendant components that subscribe to this context.
Consumer
: The Consumer
component is used to access the data provided by the Provider
higher up in the component tree. It uses a render prop function to receive and use the context data.useContext
Hook: In React 16.8 and later, you can also use the useContext
Hook to access the context value within a functional component. It simplifies the code and makes it more readable.See also: React useContext Hook - snippset
useContext
allows you to consume the context values within a functional component, making it more convenient and concise compared to using the Consumer
component.
Here's an example of using React Context and the useContext
hook:
import React, { createContext, useContext } from 'react';
// Create a context
const MyContext = createContext();
function ParentComponent() {
const contextValue = "Hello from Context!";
return (
<MyContext.Provider value={contextValue}>
<ChildComponent />
</MyContext.Provider>
);
}
function ChildComponent() {
// Consume the context using the useContext hook
const contextData = useContext(MyContext);
return <div>{contextData}</div>;
}
function App() {
return <ParentComponent />;
}
In this example, useContext
is a hook that allows ChildComponent
to access the context value provided by the ParentComponent
without needing the Consumer
component. So, while React Context itself is not a hook, it can be used in combination with hooks for more concise and modern React code.
Key features and concepts of React Router include:
Route Configuration: You can define the routes for your application using the <Route>
component. Each <Route>
element specifies a path and the component to render when that path matches the URL.
Nested Routing: React Router supports nested routes, allowing you to create complex UI structures with parent and child routes. This is useful for building layouts where certain components are always present, and others change based on the route.
Route Parameters: You can define dynamic segments in your route paths using colon-syntax like :id
, and then access those parameters in the matched component. This is handy for building routes that depend on specific data.
Navigation: React Router provides a set of components like <Link>
and <NavLink>
that enable navigation within your application. These components generate anchor tags with the appropriate href
attributes, ensuring that the application's state is updated without triggering full page reloads.
Programmatic Navigation: You can also perform programmatic navigation using the history
object provided by React Router or by using the useHistory
hook. This allows you to navigate based on user interactions or application logic.
Route Guards: React Router allows you to implement route guards or middleware that can protect certain routes from being accessed unless certain conditions are met. This is useful for adding authentication or authorization to your application.
React Router is commonly used in combination with React to create dynamic and interactive single-page applications that respond to changes in the URL, enabling users to navigate between different views or pages without a full page reload. It has become a fundamental tool in the React ecosystem for managing client-side routing.
Introduction
This set explores an efficient way to handle search functionality in a React application, focusing on the optimization of rendering performance during user input. It illustrates how separating concerns between the input field and the displayed search results helps reduce unnecessary re-renders, providing a smoother and more responsive user experience. The techniques discussed here include debouncing input, state management, and ensuring that only relevant components re-render.
Overview of the Optimization Approach
To optimize the rendering behavior in a React search feature, we separate concerns into two components: SearchInput
and SearchList
. By doing so, we achieve the following:
Benefits of the Approach
This approach provides several key benefits:
Overview: Debouncing is a technique that limits the rate at which a function is invoked, especially in cases like search input, where every keystroke could trigger an expensive operation such as an API call or a state update. In this solution, debouncing is applied to the search input field, ensuring that the search query is only processed after the user has stopped typing for a specified period, thus reducing unnecessary re-renders.
Implementation: The SearchInput
component is responsible for capturing user input. However, instead of immediately sending the input to the parent component or making API calls, we use a debouncing hook (useDebouncedValue
) to delay updates until the user stops typing for a set period (e.g., 800ms).
import React, { useState, useEffect, useCallback } from "react";
import { useDebouncedValue } from "../../services/hooks/useDebouncedValue";
function SearchInput({ initialQuery, onDebouncedQueryChange }) {
const [searchQuery, setSearchQuery] = useState(initialQuery || "");
const debouncedQuery = useDebouncedValue(searchQuery, 800);
useEffect(() => {
onDebouncedQueryChange(debouncedQuery);
}, [debouncedQuery, onDebouncedQueryChange]);
const handleInputChange = useCallback((e) => {
setSearchQuery(e.target.value);
}, []);
return (
<input
type="text"
value={searchQuery}
onChange={handleInputChange}
placeholder="Search..."
/>
);
}
This implementation ensures that the parent component (SearchPage
) receives the debounced query and only passes it to the child SearchList
when the user has stopped typing for a period, optimizing performance.
Overview: Managing state efficiently is key to ensuring React applications are performant and responsive. In this implementation, we separate the concerns of managing user input and fetching/displaying search results. The state for the search query is managed in the SearchPage
component, while the search input is handled by the SearchInput
component. This separation ensures that only the necessary components re-render when the state changes.
Implementation: The SearchPage
component manages the query state and passes it down to both the SearchInput
and SearchList
components. When the SearchInput
component detects a change in the input field, it propagates the debounced query to the SearchPage
using a callback function. This avoids unnecessary re-renders of components that don't need to be updated.
import React, { useState, useEffect } from "react";
import SearchInput from "./SearchInput";
import SearchList from "./SearchList";
function SearchPage() {
const [debouncedQuery, setDebouncedQuery] = useState("");
const handleDebouncedQueryChange = (newQuery) => {
setDebouncedQuery(newQuery);
};
return (
<div>
<SearchInput onDebouncedQueryChange={handleDebouncedQueryChange} />
<SearchList q={debouncedQuery} />
</div>
);
}
This structure ensures that the SearchList
component only re-renders when the debounced query changes, preventing unnecessary re-renders during the user’s typing.
Overview: A critical aspect of optimizing React applications is separating concerns between components. By isolating the input field and the search results, we can ensure that only the necessary component re-renders. The SearchInput
component handles the user input, while the SearchList
component is responsible for displaying the search results. This separation allows for more efficient rendering and easier maintenance.
Implementation: In this approach, the SearchInput
component is responsible for capturing the user’s search query and applying the debouncing logic. The debounced query is passed up to the SearchPage
component, which then passes it down to the SearchList
for displaying the results. This ensures that each component has a clear, focused responsibility.
function SearchInput({ onDebouncedQueryChange }) {
const [searchQuery, setSearchQuery] = useState("");
const handleInputChange = (e) => {
setSearchQuery(e.target.value);
};
useEffect(() => {
onDebouncedQueryChange(searchQuery);
}, [searchQuery, onDebouncedQueryChange]);
return (
<input
type="text"
value={searchQuery}
onChange={handleInputChange}
placeholder="Search..."
/>
);
}
function SearchList({ q }) {
const { data, isFetching } = useSearchSnipps(q);
return (
<div>
{isFetching ? <Spinner /> : data.map((item) => <FeedItem key={item.id} item={item} />)}
</div>
);
}
This structure ensures that the logic for capturing input and displaying results is handled separately, making the code easier to understand and maintain.
Overview: The final structure clearly separates the input handling from the list rendering, optimizing React performance by ensuring that components only re-render when necessary. The SearchPage
acts as the parent that coordinates state changes, while the SearchInput
and SearchList
components are responsible for their respective tasks.
Overview of the Code:
// SearchPage.js
function SearchPage() {
const [debouncedQuery, setDebouncedQuery] = useState("");
const handleDebouncedQueryChange = (newQuery) => {
setDebouncedQuery(newQuery);
};
return (
<div>
<SearchInput onDebouncedQueryChange={handleDebouncedQueryChange} />
<SearchList q={debouncedQuery} />
</div>
);
}
// SearchInput.js
function SearchInput({ onDebouncedQueryChange }) {
const [searchQuery, setSearchQuery] = useState("");
const debouncedQuery = useDebouncedValue(searchQuery, 800);
useEffect(() => {
onDebouncedQueryChange(debouncedQuery);
}, [debouncedQuery, onDebouncedQueryChange]);
return (
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search..."
/>
);
}
// SearchList.js
function SearchList({ q }) {
const { data, isFetching } = useSearchSnipps(q);
return (
<div>
{isFetching ? <Spinner /> : data.map((item) => <FeedItem key={item.id} item={item} />)}
</div>
);
}
This structure ensures efficient rendering, clear separation of concerns, and improved user experience in a React-based search interface.
This set demonstrates an effective approach to handling search functionality in a React application, focusing on optimizing performance by separating input handling and result rendering. By applying debouncing in the input field and managing state in a parent component, we ensure that only the necessary components re-render, providing a smoother experience for the user.
Introduction: This Set provides a comprehensive guide on filtering and organizing data within JavaScript, specifically focusing on arrays of objects. The concepts covered include extending arrays with additional attributes, filtering based on specific criteria, and extracting particular fields (such as names). The solutions presented here will help you efficiently manage and manipulate data, making it easier to search, filter, and retrieve information clean and organized.
Overview: In many situations, you may need to store more than just simple values in an array. Extending arrays to include attributes such as gender, language, or other metadata can add significant flexibility to your data. This Snipp shows how to structure an array to include such attributes.
Implementation:
const namesWithAttributes = [
{ name: "Norbert", gender: "male", language: "German" },
{ name: "Manuela", gender: "female", language: "Spanish" },
{ name: "Saskia", gender: "female", language: "Dutch" },
{ name: "Henrik", gender: "male", language: "Swedish" },
{ name: "Claus", gender: "male", language: "Danish" },
];
In this example, each item in the array is an object containing a name
, gender
, and language
. This structure allows you to filter and manipulate the data based on multiple attributes easily.
Overview: Filtering an array based on specific attributes (e.g., gender or language) allows you to retrieve only the relevant entries. This Snipp demonstrates how to use the filter()
method in JavaScript to extract items that meet a single criterion.
Implementation:
// Filter by gender
const maleNames = namesWithAttributes.filter(person => person.gender === "male");
console.log(maleNames);
// Output: [{ name: "Norbert", gender: "male", language: "German" }, { name: "Henrik", gender: "male", language: "Swedish" }, { name: "Claus", gender: "male", language: "Danish" }]
Here, we filter the array to return only the objects where the gender
is male
. The filter()
method is powerful for searching through arrays based on any condition.
Overview: Sometimes, filtering based on multiple attributes is necessary. This Snipp demonstrates how to combine different conditions in the filter()
method to retrieve data that satisfies more than one criterion.
Implementation:
// Filter by gender and language
const femaleSpanishNames = namesWithAttributes.filter(person => person.gender === "female" && person.language === "Spanish");
console.log(femaleSpanishNames);
// Output: [{ name: "Manuela", gender: "female", language: "Spanish" }]
In this example, the array is filtered to include only objects where the gender
is female
and the language
is Spanish
. Using multiple conditions ensures that only the most relevant data is retrieved.
Overview: After filtering data, you may only need specific fields, such as the name
attribute. This Snipp explains how to use the map()
method to extract specific fields from filtered data.
Implementation:
// Filter and extract only the names
const germanNames = namesWithAttributes
.filter(person => person.language === "German")
.map(person => person.name);
console.log(germanNames);
// Output: ["Norbert"]
Here, after filtering by language
, we use map()
to return just the name
values from the filtered array. This results in an array of names that meet the filtering criteria.
Overview: Sorting data alphabetically can help organize information in a more accessible way. This Snipp demonstrates how to sort an array of objects alphabetically based on a specific field, such as name
.
Implementation:
// Sort the names alphabetically
const sortedNames = namesWithAttributes.slice().sort((a, b) => a.name.localeCompare(b.name));
console.log(sortedNames);
// Output: [{ name: "Claus", gender: "male", language: "Danish" }, { name: "Henrik", gender: "male", language: "Swedish" }, { name: "Manuela", gender: "female", language: "Spanish" }, { name: "Norbert", gender: "male", language: "German" }, { name: "Saskia", gender: "female", language: "Dutch" }]
In this example, the slice()
method is used to create a copy of the array, and sort()
is used to sort the name
field alphabetically.
Overview: In some cases, you may want to return only specific fields from filtered data, such as extracting only the name
from a list of people. This Snipp demonstrates how to combine filtering and mapping to achieve this.
Implementation:
// Filter by gender and return only names
const maleNames = namesWithAttributes
.filter(person => person.gender === "male")
.map(person => person.name);
console.log(maleNames);
// Output: ["Norbert", "Henrik", "Claus"]
This approach combines the filter()
and map()
methods to first filter the data by gender and then extract only the name
field from the resulting objects.