I was using this code as reference to build a swipeable list of names https://gist.github.com/estaub/91e54880d77a9d6574b829cb0d3ba021
That code becomes a JSX component. I then had a parent component holding two lists, and the idea is to make an item from one of the lists go to the second list when we swipe it.
The code works fine, the problem started when I implemented the lists. When I swipe an Item, the parent component triggers a function that gets the swiped Item, removes from the first and add to the second list. The parent component gets rendered again. The problem is that the item below this one that was erased seems to be receiving the classes that was added to to make the item disappear. In other words, it's like if I was erasing two items. The erased item gets removed from the list, but the item below it still in the DOM now with the properties that hides it, so we can't see it anymore.
For some reason it seems like useRef is selecting the element below the one that has just been erased and applying the styles to it.
Any idea what I could try to solve it?
Here is my swipeable component
import React, {
useEffect, useRef,
} from 'react';
interface IOwnProps {
onSwipe?: () => void;
}
const SwipeableListItem:React.FC<IOwnProps> = ({
children, onSwipe,
}) => {
const listElementRef = useRef<HTMLDivElement | null>(null);
const wrapperRef = useRef<HTMLDivElement | null>(null);
const backgroundRef = useRef<HTMLDivElement | null>(null);
const dragStartXRef = useRef(0);
const leftRef = useRef(0);
const draggedRef = useRef(false);
useEffect(() => {
const onSwiped = () => {
if (onSwipe) {
onSwipe();
}
};
const onDragEnd = () => {
if (draggedRef.current) {
draggedRef.current = false;
const threshold = 0.3;
let elementOffsetWidth = 0;
if (listElementRef.current) {
elementOffsetWidth = listElementRef.current.offsetWidth;
}
if (leftRef.current < elementOffsetWidth * threshold * -1) {
leftRef.current = (-elementOffsetWidth * 2);
if (wrapperRef.current) {
wrapperRef.current.style.maxHeight = (0).toFixed(1);
}
onSwiped();
} else {
leftRef.current = 0;
}
if (listElementRef.current) {
listElementRef.current.className = 'BouncingListItem';
listElementRef.current.style.transform = `translateX(${leftRef.current}px)`;
}
}
};
const onDragEndMouse = (ev: MouseEvent) => {
window.removeEventListener('mousemove', onMouseMove);
onDragEnd();
};
const onDragEndTouch = (ev: TouchEvent) => {
window.removeEventListener('touchmove', onTouchMove);
onDragEnd();
};
window.addEventListener('mouseup', onDragEndMouse);
window.addEventListener('touchend', onDragEndTouch);
return () => {
window.removeEventListener('mouseup', onDragEndMouse);
window.removeEventListener('touchend', onDragEndTouch);
};
}, [onSwipe]);
const onDragStartMouse = (ev: React.MouseEvent) => {
onDragStart(ev.clientX);
window.addEventListener('mousemove', onMouseMove);
};
const onDragStartTouch = (ev: React.TouchEvent) => {
const touch = ev.targetTouches[0];
onDragStart(touch.clientX);
window.addEventListener('touchmove', onTouchMove);
};
const onDragStart = (clientX: number) => {
draggedRef.current = true;
dragStartXRef.current = clientX;
if (listElementRef.current) {
listElementRef.current.className = 'ListItem';
}
requestAnimationFrame(updatePosition);
};
const updatePosition = () => {
if (draggedRef.current) {
requestAnimationFrame(updatePosition);
}
if (listElementRef.current) {
listElementRef.current.style.transform = `translateX(${leftRef.current}px)`;
}
// fade effect
const opacity = (Math.abs(leftRef.current) / 100);
if (opacity < 1 && opacity.toString() !== backgroundRef.current?.style.opacity) {
if (backgroundRef.current) {
backgroundRef.current.style.opacity = opacity.toString();
}
}
};
const onMouseMove = (ev: MouseEvent) => {
const left = ev.clientX - dragStartXRef.current;
if (left < 0) {
leftRef.current = left;
}
};
const onTouchMove = (ev: TouchEvent) => {
const touch = ev.targetTouches[0];
const left = touch.clientX - dragStartXRef.current;
if (left < 0) {
leftRef.current = left;
}
};
return (
<div className="Wrapper" ref={wrapperRef}>
<div className="Background" ref={backgroundRef}>
<span>Remove</span>
</div>
<div
className="ListItem"
ref={listElementRef}
onMouseDown={onDragStartMouse}
onTouchStart={onDragStartTouch}
role="presentation"
>
{children}
</div>
</div>
);
};
export default SwipeableListItem;
Here is the parent component, where I handle the lists
import React, {
useEffect, useState,
} from 'react';
import SwipeableListItem from './SwipeableListItem';
interface ITest{
id:number;
}
interface IOwnProps{
list1?: ITest[];
list2?: ITest[];
}
const MyList: React.FC<IOwnProps> = ({
list1, list2,
}) => {
const [displayList1, setDisplayList1] = useState<boolean>(true);
const [list, setList] = useState<Array<Array<ITest>>>([[], []]);
useEffect(() => {
setList([list1 || [], list2 || []]);
}, [list1, list2]);
const handleSwitchList = () => {
setDisplayList1(!displayList1);
};
//without this function it works, but the item does not get removed from the list
const handleSendToSecondList = (id: number, myList1: ITest[], myList2: ITest[]) => {
const foundItem = myList2.find((s) => s.id === id);
const newList1 = myList1;
if (foundItem) {
// push item to list 1
newList1.push(foundItem);
// remove list 2
const newList2 = myList2.filter((s) => s.id !== foundItem.id);
setList([newList1, newList2]);
}
};
return (
<div>
<div>
<button
type="button"
className={`btn ${displayList1 ? 'btn-primary' : ''}`}
onClick={handleSwitchList}
>
List 1
</button>
<button
type="button"
className={`btn ${!displayList1 ? 'btn-primary' : ''}`}
onClick={handleSwitchList}
>
List 2
</button>
</div>
{
displayList1
? list[1]?.map((item) => (
<SwipeableListItem onSwipe={() => handleSendToSecondList(item.id, list[0], list[1])}>
<p>{item.id}</p>
</SwipeableListItem>
))
: list[0]?.map((item) => (
<SwipeableListItem>
<p>{item.id}</p>
</SwipeableListItem>
))
}
</div>
);
};
export default MyList;
I created this just to show what is happening
https://codesandbox.io/s/react-hooks-usestate-kbvqt?file=/src/index.js
The problem is happening here but a little different. There are two lists, if you swipe an item from the first list, an item from the second list will disappear, because it received the css properties.
Related
I've been making a game which at the end, requires the user to type their guess. To avoid confusion in my actual project, I created something in codesandbox which demonstrates the problem I'm having. I should add that the game in codesandbox isn't suppose to make much sense. But essentially you just click any box 5 times which generates a random number and when the component mounts, it also creates an array with 5 random number. At the end, you type a number and it checks if both arrays contain the key entered and colors them accordingly. The problem I'm having is that once the guess component is shown, all the hooks states return to their initial states.
Main.tsx
import { Guess } from "./Guess";
import { useHook } from "./Hook";
import { Loading } from "./Loading";
import "./styles.css";
export const Main = () => {
const {loading, count, handleClick, randArr} = useHook()
return (
<div className="main">
{!loading && count < 5 &&
<div className='click-container'>
{Array.from({length: 5}).fill('').map((_, i: number) =>
<div onClick={handleClick} className='box' key={i}>Click</div>
)}
</div>
}
{loading && <Loading count={count} />}
{!loading && count >= 5 && <Guess arr={randArr} />}
</div>
);
}
Hook.tsx
import { useEffect, useState } from 'react'
export const useHook = () => {
type guessType = {
keyNum: number
isContain: boolean
}
const [disable, setDisable] = useState(true)
const [randArr, setRandArr] = useState<number[]>([])
const [initialArr, setInitialArr] = useState<number[]>([])
const [count, setCount] = useState<number>(0)
const [loading, setLoading] = useState(true)
const [guess, setGuess] = useState<guessType[]>([])
const randomNum = () => {
return Math.floor(Math.random() * (9 - 0 + 1) + 0);
}
useEffect(() => {
const handleInitialArr = () => {
for (let i = 0; i < 5; i++) {
let num = randomNum()
setInitialArr((prev) => [...prev, num])
}
}
handleInitialArr()
}, [])
const handleClick = () => {
if (!disable) {
let num = randomNum()
setRandArr((prev)=> [...prev, num])
setCount((prev) => prev + 1)
setDisable(true)
setLoading(true)
}
}
useEffect(()=> {
const handleLoading = () => {
setTimeout(() => {
setLoading(false)
}, 500)
}
const handleRound = () => {
setDisable(false)
}
handleLoading()
handleRound()
}, [count])
const handleKeyUp = ({key}) => {
const isNumber = /^[0-9]$/i.test(key)
if (isNumber) {
if (randArr.includes(key) && initialArr.includes(key)) {
setGuess((prev) => [...prev, {keyNum: key, isContain: true}])
console.log(' they both have this number')
} else {
setGuess((prev) => [...prev, {keyNum: key, isContain: false}])
console.log(' they both do not contain this number ')
}
}
}
console.log(count)
console.log(randArr, ' this is rand arr')
console.log(initialArr, ' this is initial arr')
return {
count,
loading,
handleClick,
randArr,
handleKeyUp,
guess
}
}
Guess.tsx
import React, { useEffect } from "react";
import { useHook } from "./Hook";
import "./styles.css";
type props = {
arr: number[];
};
export const Guess: React.FC<props> = (props) => {
const { handleKeyUp, guess } = useHook();
useEffect(() => {
window.addEventListener("keyup", handleKeyUp);
return () => {
window.removeEventListener("keyup", handleKeyUp);
};
}, [handleKeyUp]);
console.log(props.arr, " this is props arr ");
return (
<div className="content">
<div>
<p>Guesses: </p>
<div className="guess-list">
{guess.map((item: any, i: number) =>
<p key={i} className={guess[i].isContain ? 'guess-num-true': 'guess-num-false'} >{item.keyNum}</p>
)}
</div>
</div>
</div>
);
};
Also, here is the codesandbox if you want to take a look for yourself: https://codesandbox.io/s/guess-numbers-70fss9
Any help would be deeply appreciated!!!
Fixed demo: https://codesandbox.io/s/guess-numbers-fixed-kz3qmw?file=/src/my-context.tsx:1582-2047
You're under the misconception that hooks share state across components. The hook will have a new state for every call of useHook(). To share state you need to use a Context.
type guessType = {
keyNum: number;
isContain: boolean;
};
type MyContextType = {
count: number;
loading: boolean;
handleClick: () => void;
randArr: number[];
handleKeyUp: ({ key: string }) => void;
guess: guessType[];
};
export const MyContext = createContext<MyContextType>(null as any);
export const MyContextProvider: FC<PropsWithChildren<{}>> = ({ children }) => {
// Same stuff as your hook goes here
return (
<MyContext.Provider
value={{ count, loading, handleClick, randArr, handleKeyUp, guess }}
>
{children}
</MyContext.Provider>
);
};
export const App = () => {
return (
<div className="App">
<MyContextProvider>
<Page />
</MyContextProvider>
</div>
);
};
export const Main = () => {
const { loading, count, handleClick, randArr } = useContext(MyContext);
...
}
export const Guess: React.FC<props> = (props) => {
const { handleKeyUp, guess } = useContext(MyContext);
...
}
Your handleKeyUp function is also bugged, a good example of why you need to type your parameters. key is a string, not a number. So the condition will always be false.
const handleKeyUp = ({ key }: {key: string}) => {
const num = parseInt(key);
if (!isNaN(num)) {
if (randArr.includes(num) && initialArr.includes(num)) {
setGuess((prev) => [...prev, { keyNum: num, isContain: true }]);
console.log(" they both have this number");
} else {
setGuess((prev) => [...prev, { keyNum: num, isContain: false }]);
console.log(" they both do not contain this number ");
}
}
};
I am trying to render a huge list of data in React. I know I can use react-window for this usecase but wanted to try if we can implement a similar window based rendering using Intersection Observer API.
I have written this component to try the same. But Here my component is rendering the whole 10,000 divs even if it is not in view port as i am iterating over the data. Is there any way to prevent rendering if the element is not there in viewport similar to react-window. Thank you in advance.
import React from 'react';
import './CustomVirtualizedList.css';
import faker from 'faker';
const generateFakeData = (() => {
const data = [];
for (let i = 0; i < 10000; i++) {
data.push({ id: i, selected: false, label: faker.address.state() })
}
return () => data;
})();
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
const ListElement = (props) => {
const [visible, setVisible] = React.useState(false);
const {containerRef} = props;
const elementRef = React.useRef();
let intersectionObserver;
const onVisibilityChange = ([entry]) => {
setVisible(entry.isIntersecting)
}
React.useEffect(() => {
console.log(props);
intersectionObserver = new IntersectionObserver(onVisibilityChange, containerRef.current);
intersectionObserver.observe(elementRef.current);
return () => {
intersectionObserver.disconnect()
}
}, [])
return <div
ref={elementRef}
style={{ backgroundColor: getRandomColor() }}
className="listElement">
{visible ? 'I am visible' : 'I am not visible'}
</div>
}
export const ListContainer = () => {
const containerRef = React.useRef();
const [data, setData] = React.useState(generateFakeData())
return (
<div className="listContainer">
{data.map(val => {
return <ListElement containerRef={containerRef} {...val} />
})}
</div>
);
};
I am trying to implement a onWheel triggered Nav for a carousel. The button nav works, while the onWheel triggers, but is somehow not accessing the initialized state of the context provider. Any insight would be appreciated. thank you.
context provider:
import CarouselContext from "../context/_carousel"
const CarouselProvider = ({ children }) => {
const [isInitialized, setIsInitialized] = useState(false)
const [carouselLength, setCarouselLength] = useState(0)
const [carouselPosition, setCarouselPosition] = useState(0)
const initializeCarousel = length => {
setCarouselLength(length)
setIsInitialized(true)
console.log(`carouselLength ${carouselLength}`)
}
const nextSlide = () => {
if (carouselPosition === carouselLength) {
setCarouselPosition(0)
} else {
setCarouselPosition(carouselPosition + 1)
}
console.log(`carouselPosition ${carouselPosition}`)
}
const previousSlide = () => {
if (carouselPosition === 0) {
setCarouselPosition(carouselLength)
} else {
setCarouselPosition(carouselPosition - 1)
}
console.log(`carouselPosition ${carouselPosition}`)
}
const state = { carouselLength, carouselPosition, isInitialized }
const methods = { initializeCarousel, nextSlide, previousSlide }
return (
<CarouselContext.Provider value={[state, methods]}>
{children}
</CarouselContext.Provider>
)
}
export default CarouselProvider
carousel structure:
return (
<Page className="works">
<CarouselProvider>
<ScrollNav>
<PreviousWorkButton />
<Carousel>
{works.map((work, index) => (
<CarouselItem key={index}>
<Work project={work} />
</CarouselItem>
))}
</Carousel>
<NextWorkButton />
</ScrollNav>
</CarouselProvider>
</Page>
)
scroll Nav (which is consoling the events are triggered, but not showing the current position of the carousel or length)
const ScrollNav = ({ children }) => {
const [, { nextSlide, previousSlide }] = useContext(CarouselContext)
const delayedScroll = useCallback(
debounce(e => changeNav(e), 500, { leading: true, trailing: false }),
[]
)
const changeNav = direction => {
if (direction === 1) {
nextSlide()
}
if (direction === -1) {
previousSlide()
}
}
const onWheel = e => {
delayedScroll(e.deltaY)
}
return <div onWheel={onWheel}>{children}</div>
}
onclick button that triggers the same event with carousel position and length persisting
const NextWorkButton = () => {
const [, { nextSlide }] = useContext(CarouselContext)
const clicked = () => {
nextSlide()
}
return (
<div className="next-work-button">
<button onClick={clicked}>
<DownArrowSvg />
</button>
</div>
)
}
edited to add console.logs in the provider as is in my local copy
console logs on click event:
carouselPosition 1
carouselLength 5
console log on wheel event (the length does not print):
carouselPosition 0
Thanks to kumarmo2 I solved this by removing the debounce and calling the event directly. I made a very hacky debounce specific to the wheel event with a timer.
my solution:
const ScrollNav = ({ children }) => {
const [, { nextSlide, previousSlide }] = useContext(CarouselContext)
const [debounce, setDebounce] = useState(false)
const timer = () => {
setTimeout(() => {
setDebounce(false)
}, 1000)
}
const changeNav = e => {
let direction = e.deltaY
if (debounce) {
return
} else if (direction >= 1) {
setDebounce(true)
timer()
nextSlide()
return
} else if (direction <= -1) {
setDebounce(true)
timer()
previousSlide()
return
}
}
return <div onWheel={changeNav}>{children}</div>
}
I think what is happening here is that, your delayedScroll is not getting updated because of useCallback. It "captures" the changeNav which in turn captures the nextSlide.
So nextSlide will be called, but since its references inside delayedScroll is not updated because of useCallback, you are facing the issue.
Can you try removing the useCallback and debounce once for delayedScroll ? and if it works, will introduce the debounce logic in the correct way.
I created a WindowPortal to open many new external windows/tabs.
https://codesandbox.io/s/modest-goldwasser-q6lig?file=/src/App.js
How can I pass props to a props.children in createPortal function?
I want pass newWindow as a prop for handling resize of new window.
import React, { useState, useRef, useEffect, useCallback, Children, cloneElement } from "react";
import { createPortal } from "react-dom";
import { create } from "jss";
import { jssPreset, StylesProvider, CssBaseline } from "#material-ui/core";
type WindowPortalProps = {
width: number;
height: number;
close: () => void;
id: number;
title: string;
};
const WindowPortal: React.FC<WindowPortalProps> = (props) => {
const [container, setContainer] = useState<HTMLElement | null>(null);
const newWindow = useRef<Window | null>(null);
const { title } = props;
const [jss, setJss] = useState<any>(null);
useEffect(() => {
// Create container element on client-side
setContainer(document.createElement("div"));
}, []);
const close = useCallback(() => {
props.close();
}, [props]);
useEffect(() => {
// When container is ready
if (container) {
setJss(create({ ...jssPreset(), insertionPoint: container }));
// Create window
newWindow.current = window.open(
"",
"",
`width=${props.width},height=${props.height},left=200,top=200,scrollbars,resizable,menubar,toolbar,location`,
) as Window;
// Append container
newWindow.current.document.body.appendChild(container);
newWindow.current.document.title = title;
const stylesheets = Array.from(document.styleSheets);
stylesheets.forEach((stylesheet) => {
const css = stylesheet as CSSStyleSheet;
const owner = stylesheet.ownerNode as HTMLElement;
if (owner.dataset.jss !== undefined) {
// Ignore JSS stylesheets
return;
}
if (stylesheet.href) {
const newStyleElement = document.createElement("link");
newStyleElement.rel = "stylesheet";
newStyleElement.href = stylesheet.href;
newWindow.current?.document.head.appendChild(newStyleElement);
} else if (css && css.cssRules && css.cssRules.length > 0) {
const newStyleElement = document.createElement("style");
Array.from(css.cssRules).forEach((rule) => {
newStyleElement.appendChild(document.createTextNode(rule.cssText));
});
newWindow.current?.document.head.appendChild(newStyleElement);
}
});
// Save reference to window for cleanup
const curWindow = newWindow.current;
curWindow.addEventListener("beforeunload", close);
// Return cleanup function
return () => {
curWindow.close();
curWindow.removeEventListener("beforeunload", close);
};
}
}, [container]);
return (
container &&
newWindow.current &&
jss &&
createPortal(
<StylesProvider jss={jss} sheetsManager={new Map()}>
<CssBaseline />
{props.children} -> how pass newWindow.current as externalWindow props?
</StylesProvider>,
container,
)
);
};
const ExpandedComponentForWindowPortal = ({ externalWindow, ...rest }) => {
const [countOfResize, setCountOfResize] = useState(0);
const doSomething = () => setCountOfResize(countOfResize + 1);
useEffect(() => {
externalWindow.addEventListener("resize", doSomething);
return () => {
externalWindow.removeEventListener("resize", doSomething);
setCountOfResize(0)
}
}, []);
return <></>;
}
const ComponentInExternalWindow = () => {
const closeWindow = () => {}
return (
<WindowPortal
title={"title"}
width={window.innerWidth}
heigth={window.innerHeigth}
key={2}
id={1}
close={closeWindow}
>
<ExpandedComponentForWindowPortal />
</WindowPortal>
)
}
I would suggest using a context for this use case.
In WindowPortalHooks:
const WindowPortalContext = createContext(null);
export const useWindowPortalContext = () => {
return useContext(WindowPortalContext);
};
and in the end of the function wrap the children with the context provider
return (
container &&
createPortal(
<WindowPortalContext.Provider
value={{
close
}}
>
{props.children}
</WindowPortalContext.Provider>,
container
)
);
Then in any child rendered by MyWindowPortal you could use useWindowPortalContext:
function Expanded(props) {
const { close } = useWindowPortalContext();
return (
<div>
<Typography align="center">{props.title}</Typography>
<button type="button" onClick={close}>
close me
</button>
</div>
);
}
So I was trying to implement a filter that is controlled by a search bar input. So I think part of the problem is that I have this filter hooked on a timer so that while the user is typing into the search bar, it isn't re-running for each letter typed in.
What it is currently doing is that after the item is typed in the search bar, the timer goes off and the filters are working but it doesn't appear that the app is re-rendering with the new filtered variable.
I suspect that it might have something to do with useEffect but I'm having trouble wrapping my head around it and it wasn't working out for whatever I was doing with it.
Here's the code:
const RecipeCards = (props) => {
const inputTypingRef = useRef(null);
let preparingElement = props.localRecipes;
let cardElement;
let elementsSorted;
const ingredientCountSort = (recipes) => {
elementsSorted = ...
}
const elementRender = (element) => {
cardElement = element.map((rec) => (
<RecipeCard
name={rec.name}
key={rec.id}
ingredients={rec.ingredients}
tags={rec.tags}
removeRecipe={() => props.onRemoveIngredients(rec.id)}
checkAvail={props.localIngredients}
/>
));
ingredientCountSort(cardElement);
};
if (inputTypingRef.current !== null) {
clearTimeout(inputTypingRef.current);
}
if (props.searchInput) {
inputTypingRef.current = setTimeout(() => {
inputTypingRef.current = null;
if (props.searchOption !== "all") {
preparingElement = props.localRecipes.filter((rec) => {
return rec[props.searchOption].includes(props.searchInput);
});
} else {
preparingElement = props.localRecipes.filter((rec) => {
return rec.includes(props.searchInput);
});
}
}, 600);
}
elementRender(preparingElement);
return (
<div className={classes.RecipeCards}>{!elementsSorted ? <BeginPrompt /> : elementsSorted}</div>
);
};
Don't worry about ingredientCountSort() function. It's a working function that just rearranges the array of JSX code.
Following up to my comment in original question. elementsSorted is changed, but it doesn't trigger a re-render because there isn't a state update.
instead of
let elementsSorted
and
elementsSorted = ...
try useState
import React, { useState } from 'react'
const RecipeCards = (props) => {
....
const [ elementsSorted, setElementsSorted ] = useState();
const ingredientCountSort = () => {
...
setElementsSorted(...whatever values elementsSorted supposed to be here)
}
Reference: https://reactjs.org/docs/hooks-state.html
I used useEffect() and an additional useRef() while restructuring the order of functions
const RecipeCards = (props) => {
//const inputTypingRef = useRef(null);
let preparingElement = props.localRecipes;
let finalElement;
const [enteredFilter, setEnteredFilter] = useState(props.searchInput);
let elementsSorted;
const [elementsFiltered, setElementsFiltered] = useState();
const refTimer = useRef();
const filterActive = useRef(false);
let cardElement;
useEffect(() => {
setEnteredFilter(props.searchInput);
console.log("updating filter");
}, [props.searchInput]);
const filterRecipes = (recipes) => {
if (enteredFilter && !filterActive.current) {
console.log("begin filtering");
if (refTimer.current !== null) {
clearTimeout(refTimer.current);
}
refTimer.current = setTimeout(() => {
refTimer.current = null;
if (props.searchOption !== "all") {
setElementsFiltered(recipes.filter((rec) => {
return rec.props[props.searchOption].includes(enteredFilter);
}))
} else {
setElementsFiltered(recipes.filter((rec) => {
return rec.props.includes(enteredFilter);
}))
}
filterActive.current = true;
console.log(elementsFiltered);
}, 600);
}else if(!enteredFilter && filterActive.current){
filterActive.current = false;
setElementsFiltered();
}
finalElement = elementsFiltered ? elementsFiltered : recipes;
};
const ingredientCountSort = (recipes) => {
console.log("sorting elements");
elementsSorted = recipes.sort((a, b) => {
...
filterRecipes(elementsSorted);
};
const elementRender = (element) => {
console.log("building JSX");
cardElement = element.map((rec) => (
<RecipeCard
name={rec.name}
key={rec.id}
ingredients={rec.ingredients}
tags={rec.tags}
removeRecipe={() => props.onRemoveIngredients(rec.id)}
checkAvail={props.localIngredients}
/>
));
ingredientCountSort(cardElement);
};
//begin render /////////////////// /// /// /// /// ///
elementRender(preparingElement);
console.log(finalElement);
return (
<div className={classes.RecipeCards}>{!finalElement[0] ? <BeginPrompt /> : finalElement}</div>
);
};
There might be redundant un-optimized code I want to remove on a brush-over in the future, but it works without continuous re-renders.