React js/ javascript code refactor for loop/switch - javascript

I have following code where I get 4 different attributes, I need to check each if its true and increment the count
var count = 0;
if (props.isApple) {
count += 1;
}
if (props.isOranges) {
count += 1;
}
if (props.isBanana) {
count += 1;
}
if (props.isJackfruit) {
count += 1;
}
console.log(count);
Is there any simple or nicer way of doing this? Any suggestions??

Looks like a job for reduce
const props = {
isApple: true,
isOranges: false,
isBanana: true,
isJackfruit: true
} // 3 true values
const checks = ["isApple", "isOranges", "isBanana", "isJackfruit"];
const count = checks.reduce((sum, check) => sum + (props[check] ? 1 : 0), 0);
console.log("count", count)
This iterates the list of checks and adds one to the sum if that property is truthy in props.
If you wanted it to be more dynamic and inclusive of all prop properties, you could define checks as...
const checks = Object.keys(props)
Since you've tagged this with reactjs, I'd use the memo hook to avoid unnecessary recalculation
import { useMemo } from "react";
const checks = ["isApple", "isOranges", "isBanana", "isJackfruit"];
const YourComponent = (props) => {
const count = useMemo(() =>
checks.reduce((sum, check) => sum + (props[check] ? 1 : 0), 0),
[ props ]
);
// etc
}

Related

React state update before setTimeOut causes timer to end immediately

I am working on a project that visualizes various path algorithms.
The method I chose to update the visual effects were to use document.querySelector(node).classList and update the class to match CSS.
This is the start of the logic, which is triggered by click event.
const visualizeDijkstra = () => {
isVisualizing = true;
const startNode = grid[startNodePos.row][startNodePos.col];
const finishNode = grid[finNodePos.row][finNodePos.col];
const visited = initiateDijkstra(grid, startNode, finishNode);
const path = getPath(finishNode);
animateVisitedAndPath(visited, path);
};
The variables visited and path are arrays containing the result of the algorithm. Each item inside the array is an object containing the coordinates for the grid.
const animateVisitedAndPath = (visited: INode[], path: INode[]) => {
let totalTime = 0;
for (let i = 0; i < visited.length; i++) {
const { row, col } = visited[i];
animateAsVisited(row, col, i, speed, startNodePos, finNodePos);
if (i === visited.length - 1) {
animatePath(path, i, speed, startNodePos, finNodePos);
}
if (i === visited.length - 1) {
totalTime = (i + path.length) * speed;
setTimeout(() => {
isVisualizing = false;
}, totalTime);
}
}
};
Inside animatedVisitedAndPath function, I use a for loop and setTimeOut function to visualize the coordinates which are inside visited and path array.
export function animateAsVisited(
row: number,
col: number,
turn: number,
speed: number,
startNodePos: NodePos,
finishNodePos: NodePos
) {
setTimeout(() => {
const { row: sRow, col: sCol } = startNodePos;
const { row: fRow, col: fCol } = finishNodePos;
if (row === sRow && col === sCol) return;
if (row === fRow && col === fCol) return;
document.getElementById(`node-${row}-${col}`)?.classList.add('visited');
}, speed * turn);
}
And here, I use the document object to access each node, and update the classList.
Everything works fine, but when I want to stop the user to interact with the program using a state variable such as isVisualizing, the moment I update the state before, or after visualizeDijkstra, all of my setTimeOut call back functions invokes immediately, updating all visualizations at the same time when visualizeDijkstra gets invoked.
I apolgize if this is a duplicated question, or the way I ask the question is inappropriate.

How to render elements based on a given value in React JS?

I want to render some stars based on a specific value, and this is what I have done so far
const Rating = ({ value }) => {
const renderStars = () => {
let stars = []; // This array should contain all stars either full, half or empty star
for (let i = 0; i < value; i++) {
if (value % 1 !== 0) {
// If the value has decimal number
} else {
// If the value has NO decimal number
}
}
return stars?.map((star) => star); // Mapping the stars array
};
return <div className="rating">{renderStars()}</div>;
};
export default Rating;
Now I have 3 icons: a full star, a half star, and an empty star. Let's say the rating value is 3.5, so what I want is to push to the stars array 3 full stars 1 half star and 1 empty star so that will be 5 stars in total. And then I can map through the array and render all the stars.
You can loop through up until your value as you're currently doing, where for each iteration you push a full star, and then after the loop is complete, check if value is a decimal to determine if you should push an additional half star:
const STAR_COUNT = 5;
const Rating = ({ value }) => {
const stars = Array.from({length: STAR_COUNT}, () => <EmptyStar />);
let i;
for (i = 0; i < value; i++) { // this will loop Math.floor(value) times
stars[i] = <FullStar />;
}
if (value % 1 != 0) // if value is a decimal, add a half star
stars[i-1] = <HalfStar />;
return <div className="rating">{stars}</div>;
};
I would also suggest wrapping this component in a call to React.memo() so that the for loop logic only runs when your value prop changes, and not what the parent rerenders.
Another, perhaps more concise way, is to use some array methods to help, such as .fill() to populate an array firstly with empty stars, then replace those empty stars up to a given index based on your value, and finally add a half star if required:
const STAR_COUNT = 5;
const Rating = ({ value }) => {
const stars = Array(STAR_COUNT).fill(<EmptyStar />).fill(<FullStar />, 0, Math.floor(value));
if (value % 1 != 0) // if value is a decimal, add a half star
stars[Math.floor(value)] = <HalfStar />;
return <div className="rating">{stars}</div>;
};
Try this in your code
let rating = 3.5;
let ratingCount = 0
for(let i=1; i<=5; i++){
if(i<=rating){
console.log("full")
ratingCount++
}
}
if(rating-Math.floor(rating) !== 0){
console.log("Half")
ratingCount++
}
for(let i=ratingCount ; i<5; i++){
console.log("empty")
}

react components does not refresh

I'm making a react app where i have to update a list twice (at least or more). Obviously i have to use useState hook for being able to edit that list whenever i want. That's pretty easy, but the problem is that i don't really know but in my app, the two lists fusion themselves. Here's an example: i have a fruit list ["apples", "bananas", "avocados"] and a social network one ["instagram", "snapchat", "facebook", "whatsapp", "telegram"]. Now, when i change the state to the first list it becomes exactly: ["apples", "bananas", "avocados", "whatsapp", "telegram"]. And that's really weird. Instead of a set it makes a somelike difference(?). By the way, here's some simplified code of what i wrote (the essential):
// Initialize in the App function
const [colors, setColors] = useState([])
const [colorpicker,setColorPicker] = useState(null)
const [inputskin, setInputSkin] = useState(localStorage.getItem('skin') !== null ? localStorage.getItem('skin') : testskin)
const inputFile = useRef(null) // rename as inputSkin
useEffect(() => {
const allcolors = []
async function getPixels(){
console.log('changed input skin')
const image = await pixels(inputskin)
for(let i = 0; i < image.height; i++){
pixelLoop:
for(let j = 0; j < image.width; j++){
const color = []
for(let k = 0; k < 4; k++) {
color.push(image.data[(i * image.width + j) * 4 + k])
}
const hex = rgbToHex(color)
for (let clr of allcolors) if (clr[0] === hex) { clr[1]++; continue pixelLoop }
if (!(allcolors.includes(hex)) && color[3] != 0) allcolors.push([hex,0])
}
}
allcolors.sort((a,b) => b[1] - a[1])
setColors(allcolors)
console.log(allcolors)
}
getPixels()
changePalette = setColorPicker
}, [inputskin])
I really can't figure out what's the problem because when i console log the colors array, that's okay, it shows me the array that i want to have in the render, but in the render it basically does not refresh itself. I tried removing by DOM the children of the components nodes, but this does not really help. Here's how i render the components:
// render
return(
...
{ colors.map(([color], i) => <Color colorstart = { color } id = { i } key = { i } colorChange = { colorChange } />) }
...
)
Here's when i load the first color array:
And here's when i load the second color array:
Here's what i would expect to have when i load the second array (I obtain this only if i reload the page):
As you can see it eats the first two color rows :(
You are not setting key property in the map function which probably causes the problem. Set unique key property in
<Color colorstart = { color } id = { i } colorChange = { colorChange } />
for example
<Color key={color[0]} colorstart = { color } id = { i } colorChange = { colorChange } />
and check if it helps.
Read more here: https://reactjs.org/docs/lists-and-keys.html

Using JavaScript to create an animated counter with React.js

I have four counters that I would like to animate (incrementing the count from 0 to a specific number) using JavaScript. My code is the following:
const allCounters = document.querySelectorAll('.counterClass');
counters.forEach(allCounters => {
const updateCounter = () => {
const end = +allCounters.getAttribute('data-target');
const count = +allCounters.innerText;
const increment = end / 200;
if (count < end) {
allCounters.innerText = count + increment;
setTimeout(updateCounter, 1);
} else {
allCounters.innerText = end;
}
};
updateCounter();
});
In React, I wasn't sure how to get it to run. I tried including the code after the using dangerouslySetInnerHTML, but that's not working. (I'm new to React).
I appreciate any assistance you could give me. Thanks so much!
Right before I posted my question, I found a plug-in (https://github.com/glennreyes/react-countup) that could do it, but wondered if it's still possible using JS. Thanks!
While using React, try to avoid direct DOM operations (both query and modifications). Instead, let React do the DOM job:
const Counter = ({start, end}) = {
// useState maintains the value of a state across
// renders and correctly handles its changes
const {count, setCount} = React.useState(start);
// useMemo only executes the callback when some dependency changes
const increment = React.useMemo(() => end/200, [end]);
// The logic of your counter
// Return a callback to "unsubscribe" the timer (clear it)
const doIncrement = () => {
if(count < end) {
const timer = setTimeout(
() => setCount(
count < (end - increment)
? count + increment
: end
),
1);
return () => clearTimeout(timer);
}
}
// useEffect only executes the callback once and when some dependency changes
React.useEffect(doIncrement, [count, end, increment]);
// Render the counter's DOM
return (
<div>{count}</div>
)
}
const App = (props) => {
// Generate example values:
// - Generate 5 counters
// - All of them start at 0
// - Each one ends at it's index * 5 + 10
// - useMemo only executes the callback once and
// when numCounters changes (here, never)
const numCounters = 5;
const countersExample = React.useMemo(
() => new Array(numCounters)
.fill(0)
.map( (c, index) => ({
start: 0,
end: index*5 + 10,
})
),
[numCounters]
);
return (
<div id="counters-container">
{
// Map generated values to React elements
countersExample
.map( (counter, index) => <Counter key={index} {...counter}/> )
}
</div>
)
}

Functional Programming: Calling a Curried Function

I'm implementing the game Tic Tac Toe/Naughts and Crosses in a functional programming style and have stumbled across a hurdle with curried functions.
I have a reoccurring pattern of functions in the form func(width, height, index) which I then wish to curry, binding width and height and leaving curriedFunc(index).
However the problem arises when I have functions that expect one of these curried functions to be defined at compile-time.
They cannot be defined at compile time, because they need input from the user to then bind the values to the function.
Below is some example code of the pattern I've encountered.
// Board indexes:
// 0 | 1 | 2
// ---+---+---
// 3 | 4 | 5
// ---+---+---
// 6 | 7 | 8
const getRowNumGivenWidth = w => i => Math.floor(i/w);
// I want to be able to declare nextIndexInRowGivenWidth() here, outside of main()
// but getRowNum() needs to be defined beforehand
const main = () => {
// User input:
const width = 3;
// ...
const getRowNum = getRowNumGivenWidth(width);
const nextIndexInRowGivenWidth = width => currentIndex => {
const rowNum = getRowNum(currentIndex);
const nextIndex = currentIndex + 1;
if (getRowNum(nextIndex) != rowNum)
result = nextIndex - width;
else
result = nextIndex;
return result;
};
const nextIndexInRow = nextIndexInRowGivenWidth(width);
const board = [0, 1, 2, 3, 4, 5, 6, 7, 8];
board.map(x => console.log(x, " -> ", nextIndexInRow(x)));
// ...
}
main();
The only way I can think of solving this is to pass the curried function as an argument (to nextIndexInRowGivenWidth() in this example).
However I don't think this is ideal as if a function requires a few similarly curried functions at run-time, it quickly becomes unwieldy to define and curry said function.
The ideal solution would be if I could somehow make the binding of the values dynamic, suppose I could put the declaration getRowNum = getRowNumGivenWidth(width); before main(). This way I could call something like getRowNum(someInt) to initialise getRowNum() which I could then use in other functions that are already expecting it to be defined.
As this is a reoccurring pattern in my code, I was wondering if there is a design pattern to achieve this.
I think you are looking for
const getRowNumGivenWidth = w => i => Math.floor(i/w);
const nextIndexInRowGivenWidth = width => {
const getRowNum = getRowNumGivenWidth(width);
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
return currentIndex => {
const nextIndex = currentIndex + 1;
if (getRowNum(nextIndex) != getRowNum(currentIndex))
return nextIndex - width;
else
return nextIndex;
};
};
const main = () => {
// User input:
const width = 3;
const nextIndexInRow = nextIndexInRowGivenWidth(width);
// ...
}
Alternatively, you could define that nextIndexInRowGiven… function not with the width as the first curried parameter, but with getRowNum itself as the parameter:
const getRowNumGivenWidth = w => i => Math.floor(i/w);
const nextIndexInRowGivenRowNumGetter = getRowNum => currentIndex => {
const nextIndex = currentIndex + 1;
if (getRowNum(nextIndex) != getRowNum(currentIndex))
return nextIndex - width;
else
return nextIndex;
};
const main = () => {
// User input:
const width = 3;
const nextIndexInRow = nextIndexInRowGivenRowNumGetter(getRowNumGivenWidth(width));
// ...
}

Categories

Resources