Reputation: 3387
For reference, check out this StackBlitz Link
I have css animation @keyframes
class..
/*
Animation classes.
*/
.example-enter {
animation: flasher 0.5s;
}
@keyframes flasher{
from {
opacity: 0.2;
}
to {
opacity: 1;
}
}
I am applying this .example-enter
css class to div when array.map
is having new added data. like below..
<div className="example-enter"> ... </div>
The question is when I added new Array to state, react will create new div, and gives animation to last added div only. But I want very first element to be animated, not last one when added new state to array.
You can check out demo in above stackblitz link to check out more details and see there when we add new list only last div is animated but i want to animate very first element to animate because that first div element is i added to array. using.. setTodoState( prev => [newTodoState, ...prev ]);
Upvotes: 1
Views: 3933
Reputation: 5288
I guess the problem is on this line, you're using index
as key for your LineItem
.
todoState.map((item, index) => <LineItem key={index} todoList={item} index={index}/>)
React does not recomment using indexes for keys, read more here.
You need to have some sort of id to uniquely identify your todo items and use that id
as key when you do your todoState.map((item) => ...
. So you'll have to make your todo item as an object
and not a string
.
In your TodoForm
, we'll add a generateId
and use this functions return value for our new todo's id
.
TodoForm.tsx
function generateId() {
// https://stackoverflow.com/a/8084248/8062659
return Math.random().toString(36).substring(7);
}
const sendTodoItem = (e) => {
if(todoInput.trim().length > 0){
const text = todoInput.trim();
const id = generateId();
addNewTodoState({ text , id }); // notice here
setIsDisabled(false);
setTodoInput('');
}else{
setIsDisabled(true);
}
}
Now on you TodoListLineItem
, we'll remove the index
from map
and use the current todo's id
as key.
TodoListLineItem.tsx
todoState.map((item) => <LineItem key={item.id} todoList={item}/>)
We also have to update your Line-Item
to display the text
property of the current todo.
Line-Item.tsx
<p>{todoList.text}</p>
Check this link for a demo.
--
Because we're using now an object
as a todo item, I believe you should also make changes on some parts of the app, like the model and the delete feature. But the changes above should be sufficient to achieve what you want as described on your post.
Upvotes: 2
Reputation: 71
A simple approach is to add the animation class with useEffect and remove it after the duration of the animation.
import React, {useEffect, useState} from 'react'
const YourApp = props => {
const [animate, setAnimate] = useState(false)
const [todoState, setTodoState] = useState([])
useEffect(() => {
setAnimate( true )
setTimeout( () => { setAnimate( false ) }, 500 )
}, [todoState])
return <div className={ animate ? "example-enter" : null } > ... </div>
}
Each time you update todoState, it will apply the animation class for 0.5s basically.
Upvotes: 2