Create a simple React component that takes in a number as input and returns a list of all the prime numbers that are less than or equal to the input number.
Before you dive into the final output, I want to encourage you to take some time to work through the exercise yourself. I believe that active learning is the most effective way to learn and grow as a developer.
So, grab a pen and paper, fire up your code editor, and get ready to dive into the React Prime Number Finder exercise. Once you have completed the exercise, feel free to return to this blog post to compare your solution to mine.
Exercise implementation:
import React, { useState } from 'react';
function PrimeNumberList() {
const [inputValue, setInputValue] = useState('');
const [primeNumbers, setPrimeNumbers] = useState([]);
const handleInputChange = (event) => {
setInputValue(event.target.value);
};
const handleButtonClick = () => {
const inputNumber = parseInt(inputValue);
const primes = [];
for (let i = 2; i <= inputNumber; i++) {
let isPrime = true;
for (let j = 2; j < i; j++) {
if (i % j === 0) {
isPrime = false;
break;
}
}
if (isPrime) {
primes.push(i);
}
}
setPrimeNumbers(primes);
};
return (
<div>
<input type="text" value={inputValue} onChange={handleInputChange} />
<button onClick={handleButtonClick}>Find Primes</button>
<ul>
{primeNumbers.map((prime) => (
<li key={prime}>{prime}</li>
))}
</ul>
</div>
);
}
export default PrimeNumberList;
In this example, the component uses the useState
hook to manage two pieces of state: the input value (inputValue
) and the list of prime numbers (primeNumbers
).
The handleInputChange
function is called whenever the user types in the input field and updates the inputValue
state to reflect the new value.
The handleButtonClick
function is called when the user clicks the "Find Primes" button. It uses a simple algorithm to find all the prime numbers less than or equal to the input value and sets the primeNumbers
state to the resulting array.
Finally, the component renders the input field, button, and a list of the prime numbers. The map
function is used to generate the list of items from the primeNumbers
array.
You can modify the input
element to only accept numbers by setting the type
attribute to "number"
. You can also add the min
attribute to ensure that only positive numbers are accepted.
Here's the modified code for the input
element:
<input type="number" value={inputValue} onChange={handleInputChange} min={1} />
By setting the type
attribute to "number"
, the input field will only accept numerical input. And by setting the min
attribute to 1
, we ensure that only positive numbers are allowed.
Additionally, you can add some validation to the handleInputChange
function to ensure that the input value is a valid number. Here's an updated version of that function:
const handleInputChange = (event) => {
const input = event.target.value;
if (input === '' || /^[0-9\b]+$/.test(input)) {
setInputValue(input);
}
};
In this updated function, we use a regular expression to test whether the input value consists of only digits. If it does, we update the state with the new input value. If not, we do nothing.
The \b
in the regular expression matches a backspace character, which allows the user to delete characters from the input field.
One issue that can arise with the current implementation is that if the user enters a very large number, the algorithm used to find prime numbers will take a long time to execute, potentially causing the browser to become unresponsive.
To address this issue, we can modify the algorithm to use a more efficient approach for finding prime numbers, such as the Sieve of Eratosthenes. This algorithm works by creating a boolean array of size n+1
, where n
is the input number and initially sets all elements to true
. We then iterate over the array, starting from 2
, and for each prime number we mark all of its multiples as false
. The remaining true
elements in the array are the prime numbers.
Here's an updated version of the handleButtonClick
function that uses the Sieve of Eratosthenes algorithm:
const handleButtonClick = () => {
const inputNumber = parseInt(inputValue);
if (isNaN(inputNumber) || inputNumber <= 1) {
setPrimeNumbers([]);
return;
}
const primes = [];
const isPrime = Array(inputNumber + 1).fill(true);
isPrime[0] = false;
isPrime[1] = false;
for (let i = 2; i <= Math.sqrt(inputNumber); i++) {
if (isPrime[i]) {
for (let j = i * i; j <= inputNumber; j += i) {
isPrime[j] = false;
}
}
}
for (let i = 2; i <= inputNumber; i++) {
if (isPrime[i]) {
primes.push(i);
}
}
setPrimeNumbers(primes);
};
In this updated function, we first check whether the input value is a valid number and greater than 1
. If not, we set the primeNumbers
state to an empty array and return early.
If the input value is valid, we create an array isPrime
of size n+1
and fill it with true
. We then iterate over the array, starting from 2
, and for each prime number we mark all of its multiples as false
. Finally, we iterate over the array again and add all the true
elements to the primes
array, which we then set as the primeNumbers
state.
By using the Sieve of Eratosthenes algorithm, we can efficiently find all the prime numbers less than or equal to the input value, even for very large inputs.
Here's the final code that addresses both the input validation and performance issues
import React, { useState } from "react";
const PrimeNumberList = () => {
const [inputValue, setInputValue] = useState("");
const [primeNumbers, setPrimeNumbers] = useState([]);
const handleInputChange = (event) => {
const input = event.target.value;
if (input === '' || /^[0-9\b]+$/.test(input)) {
setInputValue(input);
}
};
const handleButtonClick = () => {
const inputNumber = parseInt(inputValue);
if (isNaN(inputNumber) || inputNumber <= 1) {
setPrimeNumbers([]);
return;
}
const primes = [];
const isPrime = Array(inputNumber + 1).fill(true);
isPrime[0] = false;
isPrime[1] = false;
for (let i = 2; i <= Math.sqrt(inputNumber); i++) {
if (isPrime[i]) {
for (let j = i * i; j <= inputNumber; j += i) {
isPrime[j] = false;
}
}
}
for (let i = 2; i <= inputNumber; i++) {
if (isPrime[i]) {
primes.push(i);
}
}
setPrimeNumbers(primes);
};
return (
<div>
<label>
Enter a number:
<input
type="number"
value={inputValue}
onChange={handleInputChange}
min={1}
/>
</label>
<button onClick={handleButtonClick}>Generate primes</button>
<ul>
{primeNumbers.map((number) => (
<li key={number}>{number}</li>
))}
</ul>
</div>
);
};
export default PrimeNumberList;
Here's the TypeScript version of the final code
import React, { useState } from "react";
const PrimeNumberList: React.FC = () => {
const [inputValue, setInputValue] = useState<string>("");
const [primeNumbers, setPrimeNumbers] = useState<number[]>([]);
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const input = event.target.value;
if (input === '' || /^[0-9\b]+$/.test(input)) {
setInputValue(input);
}
};
const handleButtonClick = () => {
const inputNumber = parseInt(inputValue);
if (isNaN(inputNumber) || inputNumber <= 1) {
setPrimeNumbers([]);
return;
}
const primes: number[] = [];
const isPrime: boolean[] = Array(inputNumber + 1).fill(true);
isPrime[0] = false;
isPrime[1] = false;
for (let i = 2; i <= Math.sqrt(inputNumber); i++) {
if (isPrime[i]) {
for (let j = i * i; j <= inputNumber; j += i) {
isPrime[j] = false;
}
}
}
for (let i = 2; i <= inputNumber; i++) {
if (isPrime[i]) {
primes.push(i);
}
}
setPrimeNumbers(primes);
};
return (
<div>
<label>
Enter a number:
<input
type="number"
value={inputValue}
onChange={handleInputChange}
min={1}
/>
</label>
<button onClick={handleButtonClick}>Generate primes</button>
<ul>
{primeNumbers.map((number) => (
<li key={number}>{number}</li>
))}
</ul>
</div>
);
};
export default PrimeNumberList;
Thanks for taking this JavaScript exercise!
I hope this JavaScript exercise will help you unlock the full potential of React and build high-performance web applications.
If you have any questions or comments, feel free to reach out to me on Twitter(@rajeshtomjoe), or follow me for more updates.
And if you'd like to receive more exercise on JavaScript, be sure to subscribe to my blog.