React Reveal not working for array of data - reactjs

Can't use React Reveal on array of data with .map() to produce effect from documentation.
https://www.react-reveal.com/examples/common/
Their documentation gives a nice example
<Fade left cascade>
<div>
<h2>React Reveal</h2>
<h2>React Reveal</h2>
<h2>React Reveal</h2>
</div>
</Fade>
I want to produce the same CASCADE effect with my data
<React.Fragment>
{projects.filter(project => project.category === category)
.map((project, index) => {
return (
<ProjectThumb key={index} project={project}
showDetails={showDetails}/>
)
})}
</React.Fragment>
The effect I'm getting is that the entire ProjectThumb component list fades in in one group, I need them to fade in individually and as i scroll. Thanks in advance.

Pass react-reveal props to your React component. It will work.
<Fade left cascade>
<div>
{
projects
.filter(project => project.category === category)
.map((project, index) => (
<ProjectThumb key={index} project={project} showDetails={showDetails} />
))
}
</div>
</Fade>
In your ProjectThumb.js
const ProjectThumb = props => {
return <Whatever {...props}>{...}</Whatever>
}

Related

react-beautiful-dnd pull out the anonymous function from React Draggable component

I need to pull out the anonymous function from Draggable into a callback function using the useCallback(..., []) hook supplying the dependencies provided, snapshot if possible.
Upon investigation we have found that the 3rd party component Draggable is using an anonymous function that essentially recreates the function on the stack for each step in the list. When this happens for a route with 500 steps the stack trace gets increased by 500 bringing us closer to a stack overflow.
React has a virtual dom, and if the child component has not changed it will not re-render it. But anonymous functions make it re-render every time, but if the anonymous function is pushed down one more layer it won't see it as changed.
Here is my implemented code.
const StepContainer = ({
step,
index,
draggedDescription,
isSelected,
selectedTaskIdsCount
}: Props) => {
return (
<Box data-testid="stepContainer" className={stepContainer}>
<Box className={'hoverstyle'}>
<Draggable draggableId={step.key} index={index} key={step.key}>
{(
provided: DraggableProvided,
snapshot: DraggableStateSnapshot
) => (
<div
data-testid="clickHandler"
{...provided.draggableProps}
ref={provided.innerRef}
data-is-dragging={
snapshot.isDragging && !snapshot.isDropAnimating
}
className={snapshot.isDragging ? `${dragging}` : ''}
>
{snapshot.isDragging ? (
<DraggedStepAsset
draggedDescription={draggedDescription}
stepCount={
selectedTaskIdsCount > 1
? selectedTaskIdsCount
: 1
}
/>
) : (
<AccordionSteps
isSelected={isSelected}
provided={provided}
step={step}
/>
)}
</div>
)}
</Draggable>
</Box>
</Box>
);
};
The idea is to pull out the anonymous function into a callback function using the useCallback(..., []) hook supplying the dependecies provided, snapshot if possible. Then we can wrap the useCallback function with a useMemo() to ensure React memoizes it, this could potentially enable react to reuse the same function on the stack for every step-component instead of adding it for each step-component. Making our code much more optimized.
Please help me to resolve this issue.
Hope the below snippet works for you.
const StepContainer = ({
step,
index,
draggedDescription,
isSelected,
selectedTaskIdsCount
}: Props) => {
return (
<Box data-testid="stepContainer" className={stepContainer}>
<Box className={'hoverstyle'}>
<Draggable draggableId={step.key} index={index} key={step.key}>
{DraggableContentCallback}
</Draggable>
</Box>
</Box>
);
};
const DraggableContent = (
provided: DraggableProvided,
snapshot: DraggableStateSnapshot
) => (
<div
data-testid="clickHandler"
{...provided.draggableProps}
ref={provided.innerRef}
data-is-dragging={
snapshot.isDragging && !snapshot.isDropAnimating
}
className={snapshot.isDragging ? `${dragging}` : ''}
>
{snapshot.isDragging ? (
<DraggedStepAsset
draggedDescription={draggedDescription}
stepCount={
selectedTaskIdsCount > 1
? selectedTaskIdsCount
: 1
}
/>
) : (
<AccordionSteps
isSelected={isSelected}
provided={provided}
step={step}
/>
)}
</div>
);
const DraggableContentCallback = useCallback(DraggableContent, []);

React.js : index.js:1 Warning: Each child in a list should have a unique "key" prop

I'm doing an app, in the fronted i'm using react, and whenever that i use the map function, i get these errors.
And you would think... Well, that's because you didn't assing any key to your container, and the truth is that, i did... but the errores keeps showing.
Let me show you the code
{specificPhotos.map((photo, i) => {
return (
<>
{/* The Photo */}
<PhotoWrapper key={photo.id}>
{/* For Images */}
{photo.photo.charAt(5) === "i" && (
<img src={photo.photo} />
)}
{/* For Videos */}
{photo.photo.charAt(5) === "v" && (
<>
<video src={photo.photo} />
</>
)}
{/* For Videos Too */}
{photo.photo.charAt(5) === "v" && (
<>
<VideoPlay>
<VideoPlayIcon />
</VideoPlay>
</>
)}
And the other time i used map fn as well
{singlePhoto.comments.map(comment => {
return (
<>
<ContainComment key={comment.id}>
{/* Photo */}
<PresentationImg
style={{ width: "100%", maxHeight: "50px" }}
area="perfil"
>
<CommentPresentation>
<img src={comment.presentation} />
</CommentPresentation>
</PresentationImg>
{/* Body */}
<TheComment>
<h2>
{comment.user} <span>{comment.body}</span>
</h2>
</TheComment>
So, where am i supossed to do with the key? maybe i did something wrong... I don't know, just help me and thanks for your time !
The problem is your second code snippet.
Option 1:
If you can remove the React fragment i.e <></>
Option 2:
However, If the React fragment is serving the purpose of wrapping the elements together, you can replace it with a
<div key={comment.id}><div/>
this means that you can remove the key={comment.id} on the <ContainComment key={comment.id}>
Your key should be on the top most element, which in this case are the fragments. See keyed-fragments
<React.Fragment key={comment.id}>
<ContainComment>
you need to add a key to the fragment right after the return (<>), but unfortunately you can only add a key if it is imported as Fragment (<Fragment key={}></Fragment>), instead of using the short syntax.
As mentioned above, the React fragment <></> is the issue.
Why is this:
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
different from this?
const todoItems = todos.map((todo) =>
<>
<li key={todo.id}>
{todo.text}
</li>
</>
);
Because the fragment needs the key on it (or removed) in the 2nd snippet to prevent the map error.
If you need to keep the fragment to avoid adding an unnecessary div, you could also do:
const todoItems = todos.map((todo) =>
<React.Fragment key={todo.id}>
<li>
{todo.text}
</li>
</React.Fragment>
);
Source: https://reactjs.org/docs/lists-and-keys.html#keys

How to avoid React Hook UseState to share the states?

I may have a bad title for this question, but here's my situation.
I use a chunk of json to render a list. The list item can be expanded and showed the sub list if it has children property. The json structure includes two arrays and each array contains more sub-arrays. I use tabs to switch arrays.
I use useState to manage the value isExpanded of each individual sub-array component. but it seems like the state isExpaned is shared for all tabs.
The state isExpanded remains same even if I switch to another tab. In other words, why the sub-list keep expanded when I switch to another tab?
In addition, why the expanded sub-list of each tab overlaps each other. They should keep 'close' when I switch to another tab because I set the initial state to false already. (const [isExpand, setIsExpand] = useState(false))
const ListItem = ({name, children}) => {
const [subList, setSubList] = useState(null)
const [isExpand, setIsExpand] = useState(false)
const handleItemClick = () => {
children && setIsExpand(!isExpand)
console.log(isExpand)
}
useEffect(() => {
isExpand && children && setSubList(children)
}, [isExpand, children])
return (
<div className='list-wrapper'>
<div className='list-item'>
{name}
{
children &&
<span
className='expand'
onClick={() => handleItemClick()}>
{isExpand ? '-' : '+'}
</span>
}
</div>
<div className='list-children'>
{
isExpand && subList && subList.map((item, index) =>
<ListItem key={index} name={item} />
)
}
</div>
</div>
)
}
Here's the codesanbox, anyone helps?
It seems like React is confused due to index being used as ListeItem key.
(React will try to "share" isExpanded state as they look the same according to the key you specified)
You could change the key from key={index}
<div className="contents">
{contents &&
contents.children &&
contents.children.map((item, index) => (
<ListItem
...... 👇 ....
key={index}
name={item.name}
children={item.children}
/>
))}
</div>
to use more distinct key, item.name
<div className="contents">
{contents &&
contents.children &&
contents.children.map(item => (
<ListItem
...... 👇 ....
key={item.name}
name={item.name}
children={item.children}
/>
))}
</div>
Check out the forked sandbox.
https://codesandbox.io/s/soanswer57212032-9ggzj

React-virtualized List selected item styling only fire when scroll up

I'm using React-virtualized to show the data as list.
I add selected styling for list item, it supposes highlight the item once it got click.
The current problem is onClick is fired, but selcted styling only shows when scroll up the list.
List component
<div className={styles.autoSizerContainer}>
<AutoSizer>
{({width, height}) => {
// Selected customer styling only fire after scroll
return (
<List
width={width}
height={height}
rowHeight={50}
rowRenderer={this.renderRow}
rowCount={rowCount}
overscanRowCount={3}
className={styles.list}
/>
)
}}
</AutoSizer>
</div>
List item
private renderRow = ({index, key, style}: ListRowProps) => {
const data = this.props.dataList[index];
return (
<div style={style} key={data.id}>
<div className={styles.listItem}>
<div>data.name</div>
<Item key={data.id}
isDataSelected={this.state.selectedId === data.id}
/> //return true will show selected styling
</div>
</div>
)
};
"react-virtualized": "^9.21.0",
"react": "^16.8.4"
Any ideas are welcome!
Thanks!!!
React-Virtualized will only re-render your component when one of the props provided by ListRowProps changes. It doesn't know that your render method uses this.props.dataList and this.state.selectedId internally. You will need to do one of two things.
Explicitly tell the List to redraw when this.state.selectedId changes. The List exposes an api for this purpose.
Use something like React's Context api to provide the necessary data in a way such that changes can be detected. Something like this should work:
const {Provider, Consumer} = React.createContext<number | null>(null);
<div className={styles.autoSizerContainer}>
<AutoSizer>
{({width, height}) => {
// Selected customer styling only fire after scroll
return (
<Provider value={this.state.selectedId}>
<List
width={width}
height={height}
rowHeight={50}
rowRenderer={this.renderRow}
rowCount={rowCount}
overscanRowCount={3}
className={styles.list}
/>
</Provider>
)
}}
</AutoSizer>
</div>
private renderRow = ({index, key, style}: ListRowProps) => {
const data = this.props.dataList[index];
return (
<Consumer>
{(selectedId) =>
<div style={style} key={data.id}>
<div className={styles.listItem}>
<div>data.name</div>
<Item key={data.id}
isDataSelected={selectedId === data.id}
/> //return true will show selected styling
</div>
</div>
}
</Consumer>
)
};

Spreading props to a list of Components

I'm trying to send a bunch of props to a Component.
In console.logs I noted that everything is working as I expected, every object has its correct value, every spread operation is working. But my Cards doesn't show in the page.
Is this way correct?
return (
<div>
{this.state.articles.forEach((card) => {
<ArticleCard {...card} />
})}
</div>
)
Image showing the problem
Array.forEach does not return anything. You need to use Array.map. Also you should be returning the component to be rendered in the callback.
return (
<div>
{this.state.articles.map((card) => (
<ArticleCard {...card} />
)}
</div>
)
Instead of using forEach, you should use map function on array, and with return keyword to return each and every ArticleCard
return(
<div>
{this.state.articles.map((card) => {
return <ArticleCard {...card} />
})}
</div>
)

Resources