使用 React-spring useTransition 的动画在递归循环中无法按预期工作
Posted
技术标签:
【中文标题】使用 React-spring useTransition 的动画在递归循环中无法按预期工作【英文标题】:Animation using React-spring useTransition not working as expected inside a recursive loop 【发布时间】:2021-09-26 11:43:48 【问题描述】:我无法在我正在处理的评论部分中对 div 应用过渡。注释部分是递归渲染的,每个部分都有一个 useTransition 实例。当点击任何 cmets 的回复按钮时,回复表单应转入。单击显示/隐藏 cmets 时也是如此,它的所有子项都应转出,但转换效果将应用于所有 div。我做了一个codepen to illustrate the problem. 请指出我正确的方向。谢谢。
import React, useState, useEffect from "https://cdn.skypack.dev/react@17.0.1";
import * as ReactDOM from "https://cdn.skypack.dev/react-dom@17.0.1";
import styled from "https://cdn.skypack.dev/styled-components@5.3.0";
import useSpring, useTransition, animated from "https://cdn.skypack.dev/react-spring@9.2.4";
///////// Styled Components ///////////////////////////////
const CommentDisplay = styled(animated.div)`
max-height: 100%;
margin: 10px 0px 0px 25px;
position: relative;
img
width: 25px;
height: 25px;
margin: 1px 10px 0px 0px;
border-radius: 50%;
border: 1px solid gray;
`;
const TopBarWrapper = styled.div`
display: flex;
position: relative;
z-index: -1;
`;
const BorderDiv = styled.div`
position: absolute;
border-left: 1px solid gray;
height: calc(100% - 25px);
width: 100%;
margin-left: 12px;
bottom: 0px;
pointer-events: none;
`;
const CommentBody = styled.p`
overflow-wrap: break-word;
word-wrap: break-word;
-ms-word-break: break-all;
word-break: break-word;
padding-left: 35px;
`;
const BottomBarWrapper = styled.div`
grid-area: bottomBar;
display: flex;
flex-direction: row;
padding-left: 35px;
`;
const Reply = styled.div`
color: rgba(7, 7, 7, 0.65);
cursor: pointer;
padding: 8px 8px 8px 0px;
font-size: 14px;
&:hover
color: black;
`;
const VoteUp = styled.div`
cursor: pointer;
padding: 8px;
&:hover
background-color: #e5f4fb;
svg
width: 16px;
height: 15px;
margin-right: 4px;
span
font-size: 13px;
`;
const VoteDown = styled.div`
cursor: pointer;
padding: 8px;
&:hover
background-color: #e5f4fb;
svg
width: 16px;
height: 15px;
margin-right: 4px;
span
font-size: 13px;
`;
const Form = styled.form`
display: grid;
//grid-template-columns: 90%;
grid-gap: 1.5rem;
grid-area: main_comment_body;
`;
const FormWrapper = styled.div`
display: $props => props.rows[props.commentid] == "true" ? "grid" : "grid";
grid-template-columns: minmax(min-content, max-content) 1fr;
grid-template-rows: minmax(50px, 1fr) minmax(min-content, max-content);
grid-template-areas:
"main_comment_img main_comment_body "
"main_comment_img main_comment_buttons";
margin: 0px 50px 0px 85px;
z-index: $props => props.rows[props.commentid] == "true" ? "1" : "-1";
opacity: $props => props.rows[props.commentid] == "true" ? "1" : "0";
height: $props => props.rows[props.commentid] == "true" ? "initial" : "0px";
min-height: $props => props.rows[props.commentid] == "true" ? "100px" : "0px";
position: relative;
top: $props => props.rows[props.commentid] == "true" ? "7px" : "-100px";
left: 0;
background-color: F4F4F4;
transition: all .05s ease 0s;
img
width: 25px;
height: 25px;
grid-area: avatar;
margin: 1px 10px 0px 0px;
border-radius: 50%;
border: 1px solid gray;
grid-Area: main_comment_img;
`;
const CommentSection = () =>
//in the useEffect hook, an ajax call gets all the comments from the rails server (in this case, it's a hard coded //"allComments" object below) and recursivley gets the id of every comment, reply, reply of reply etc.. and builds an //object in the format of "comment id" : " false".
//
// example showMore initial state
// "207":"false",
// "208":"false",
// "209":"false",
// "210":"false",
// "211":"false",
// "212":"false",
// "213":"false",
// "214":"false"
//
// when the "show/hide" comments button is clicked, it sets the id of all its children to "true" in the showMore state //variable, causing the css to //toggle it out of view.
const [showMore, setShowMore] = useState();
const [rows, setRows] = useState();
// "rows" is identical to "showMore" but is used when the "reply" button in clicked, causing the reply form appear.
//response from server containing all the comments and its nested replies etc..
const allComments = [
"id": 295,
"body": "This is a First level comment to the main Story blah blah...",
"created_at": "2021-07-16T17:17:10.410Z",
"updated_at": "2021-07-16T17:17:10.410Z",
"original_comment_author": null,
"parent_id": null,
"ancestry": null,
"date": "less than a minute",
"comment_number": 256,
"reply": false,
"user_id": 1,
"commentable_type": "Story",
"commentable_id": 1,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "Jimmy",
"comments": [
"id": 296,
"body": "this is the first reply to the main comment ",
"created_at": "2021-07-16T17:17:49.585Z",
"updated_at": "2021-07-16T17:17:49.585Z",
"original_comment_author": "undefined",
"parent_id": 295,
"ancestry": "295",
"date": "less than a minute",
"comment_number": 257,
"reply": true,
"user_id": 1,
"commentable_type": "Comment",
"commentable_id": 295,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "izzy",
"comments": [
"id": 298,
"body": "Reply to the reply (3rd level)",
"created_at": "2021-07-16T17:22:46.088Z",
"updated_at": "2021-07-16T17:22:46.088Z",
"original_comment_author": "undefined",
"parent_id": 296,
"ancestry": "295/296",
"date": "less than a minute",
"comment_number": 259,
"reply": true,
"user_id": 1,
"commentable_type": "Comment",
"commentable_id": 296,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "Noel",
"comments": [
"id": 299,
"body": "another reply to a reply (4th level) etc...",
"created_at": "2021-07-16T17:23:10.561Z",
"updated_at": "2021-07-16T17:23:10.561Z",
"original_comment_author": "undefined",
"parent_id": 298,
"ancestry": "295/296/298",
"date": "less than a minute",
"comment_number": 260,
"reply": true,
"user_id": 1,
"commentable_type": "Comment",
"commentable_id": 298,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "Mitch",
"comments": []
]
]
,
"id": 297,
"body": "this is a second reply to the main comment .... ",
"created_at": "2021-07-16T17:18:59.249Z",
"updated_at": "2021-07-16T17:18:59.249Z",
"original_comment_author": "undefined",
"parent_id": 295,
"ancestry": "295",
"date": "less than a minute",
"comment_number": 258,
"reply": true,
"user_id": 1,
"commentable_type": "Comment",
"commentable_id": 295,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "mike",
"comments": []
]
,
"id": 294,
"body": "This is another First Level comment to the main story ... ",
"created_at": "2021-07-16T17:16:19.314Z",
"updated_at": "2021-07-16T17:16:19.314Z",
"original_comment_author": null,
"parent_id": null,
"ancestry": null,
"date": "less than a minute",
"comment_number": 255,
"reply": false,
"user_id": 1,
"commentable_type": "Story",
"commentable_id": 1,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "Natalie",
"comments": []
]
;
useEffect( () =>
addAllCommentsToStateForReplyButtonToWork(allComments)
addAllCommentsToStateForShowMoreButtonToWork(allComments)
,[]);
const getReplyArray = (childrenCommentArray) =>
let tempArray = []
childrenCommentArray.map( (x, i) =>
x.id
tempArray.push(x.id + ", ")
);
return tempArray.length > 0 ? tempArray : "blank"
;
function addAllCommentsToStateForReplyButtonToWork(c)
let newArray = [];
let newState = ;
function getAllId(arr, key)
arr.forEach(function(item)
for (let keys in item)
if (keys === key)
newArray.push(item[key])
else if (Array.isArray(item[keys]))
getAllId(item[keys], key);
);
getAllId(c, 'id');
newArray.forEach(function(item)
newState[item] = "false";
)
setRows(newState);
function addAllCommentsToStateForShowMoreButtonToWork(c)
let newArray = [];
let newState = ;
function getAllId(arr, key)
arr.forEach(function(item)
for (let keys in item)
if (keys === key)
newArray.push(item[key])
else if (Array.isArray(item[keys]))
getAllId(item[keys], key)
)
getAllId(c, 'id')
newArray.forEach(function(item)
newState[item] = "false"
)
setShowMore(newState);
const handleSubmitClick = (e) =>
e.preventDefault()
const hideCommentsOrShowComments = (childrenCommentArray) =>
let tempArray = []
let numOfTrue = 0
let numOfFalse = 0
let tempShowMore =
childrenCommentArray.map( (x, i) =>
tempArray.push(x.id)
)
tempArray.forEach (x =>
if (showMore[x] == "true")
numOfTrue = numOfTrue + 1
else
numOfFalse = numOfFalse + 1
)
if (numOfTrue > 0)
return "show replies"
else
return "hide replies"
const handleShowMoreButton = (childrenCommentArray) =>
//console.log("handleShowMoreButtonfrom article.jsx------------------------")
let tempArray = []
let tempShowMore =
childrenCommentArray.map( (x, i) =>
tempArray.push(x.id)
)
tempArray.forEach (x =>
//console.log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx idididid" + x)
if (showMore[x] == "true")
//console.log("in if and x is = " + x + " and was true, changing it!")
tempShowMore[x] = "false"
else
//console.log("in else and x is = " + x + " and was false, changing it!")
tempShowMore[x] = "true"
)
setShowMore(...showMore, ...tempShowMore)
const handleReplyButton = (id) =>
if (rows[id] == "true")
setRows(...rows, [id]: "false")
else
setRows(...rows,[id]: "true")
const handleChange = () =>
//
////////////// Comment Function, Called recursively ///////////////
function Comment( item, rows, showMore, handleShowMoreButton, handleReplyButton )
const transition = useTransition(showMore[item.id],
from: opacity: 0,
enter: y: 0, opacity: 1,
leave: opacity: 0,
delay: 100
);
const nestedComments = (item.comments || []).map(com =>
return <Comment style=border: "2px solid blue" key=com.id item=com type="child" rows=rows
showMore=showMore handleShowMoreButton=handleShowMoreButton
handleReplyButton=handleReplyButton/>
);
return (
<>
transition((style,val) => val == "true" ? '' :
<CommentDisplay
style=style
key=item.id + "commentDisplay"
showMore=showMore
item=item
id=item.id >
<BorderDiv/>
<TopBarWrapper>
<img src=item.author_avatar/>
<h3 style=alignSelf: "center", fontSize: ".6em", gridArea: "nick", marginRight: "8px"> item.author_nick
</h3>
<span style=alignSelf: "center", gridArea: "date", fontSize: ".6em", color: "gray"></span>
</TopBarWrapper>
<CommentBody style=gridArea: "body", fontSize: "15px">
item.body (...the ID for this comment => item.id, and its children id's => getReplyArray(item.comments)) </CommentBody>
<BottomBarWrapper>
<Reply onClick=() => handleReplyButton(item.id)>reply</Reply>
<VoteUp>
<svg viewBox="0 0 22 20" xmlns="http://www.w3.org/2000/svg"><path key=item.id + "path1" data-id= item.id + "path1" d="M10.74.04a2.013 2.013 0 00-1.58 1.88c-.11 2.795-.485 4.45-2.283 6.946a1.272 1.272 0 00-1.065-.58h-4.55C.573 8.287 0 8.84 0 9.507v8.773c0 .667.572 1.218 1.263 1.218h4.55c.435 0 .821-.22 1.049-.548.263.204.506.387.758.533.417.24.887.384 1.532.45 1.29.128 3.403.032 8.283.052a.53.53 0 00.317-.113c1.224-.667 4.255-5.775 4.248-10.534-.026-1.138-.542-1.78-1.532-1.78H13.96c.388-2.47.131-4.738-.735-6.208C12.76.555 12.078.111 11.403.018a2.035 2.035 0 00-.663.022m2.154 7.912c-.055.28.201.58.498.58h6.934c.356.035.67.091.67.913 0 1.047-.168 2.886-1.031 5.057-.865 2.172-2.155 4.531-2.603 4.455-1.215.08-7.014.109-8.108 0-.556-.056-.818-.135-1.113-.306-.266-.152-.59-.423-1.066-.791v-7.6c2.349-2.88 2.979-5.302 3.096-8.3.338-1.495 1.702-1.082 2.179-.13.697 2.402.879 4.442.544 6.122M1.263 9.262h4.55c.148 0 .251.1.251.244v8.773c0 .144-.103.243-.252.243h-4.55c-.148 0-.251-.099-.251-.243V9.506c0-.144.103-.244.252-.244"></path>
</svg>
<span></span>
</VoteUp>
<VoteDown>
<svg viewBox="0 0 22 20" xmlns="http://www.w3.org/2000/svg"><path key=item.id + "path2" data-id= item.id + "path2" d="M11.26 19.96a2.013 2.013 0 001.58-1.881c.11-2.794.484-4.45 2.282-6.945.224.345.618.58 1.066.58h4.548c.692 0 1.264-.553 1.264-1.22V1.722c0-.668-.572-1.22-1.264-1.22h-4.548c-.436 0-.823.22-1.05.55a6.898 6.898 0 00-.759-.534c-.416-.24-.887-.384-1.531-.45C11.558-.06 9.445.037 4.564.017a.521.521 0 00-.316.114C3.023.796-.007 5.904 0 10.663c.025 1.138.541 1.78 1.532 1.78H8.04c-.39 2.47-.131 4.738.735 6.208.467.794 1.148 1.238 1.823 1.331a2.034 2.034 0 00.663-.022m-2.155-7.913c.056-.28-.202-.579-.497-.579H1.674c-.356-.035-.67-.091-.67-.913 0-1.047.166-2.886 1.031-5.057C2.9 3.326 4.19.967 4.638 1.044c1.214-.081 7.014-.109 8.108 0 .556.055.818.134 1.113.305.265.152.59.423 1.066.791v7.6c-2.349 2.88-2.979 5.302-3.096 8.3-.338 1.495-1.702 1.083-2.179.13-.697-2.402-.88-4.442-.545-6.123m11.631-1.309h-4.548c-.149 0-.252-.1-.252-.244V1.722c0-.144.103-.244.252-.244h4.548c.15 0 .253.1.253.244v8.772c0 .144-.103.244-.253.244"></path> </svg>
<span></span>
</VoteDown>
<span style=cursor: "pointer", marginLeft: "10px", fontSize: "10px", lineHeight: "40px"
onClick=() => handleShowMoreButton(item.comments)>
item.comments === undefined || item.comments.length == 0 ? "" : hideCommentsOrShowComments(item.comments)
</span>
</BottomBarWrapper>
<FormWrapper rows=rows commentid=item.id>
<img src=item.author_avatar></img>
<Form
id=item.id.toString() + "form"
className="form-inline"
onSubmit=handleSubmitClick
enctype="multipart/form-data" >
<div style=width: "100%", height: "100%" className="field" >
<textarea
style=width: "100%"
onChange=handleChange
index=1
placeholder="...reply to "
name="comment"
onKeyPress=e =>
if(e.key === 'Enter')
e.preventDefault()
value="enter a reply..."/>
</div>
</Form>
<button
form=item.id.toString() + "form"
style=marginTop: "3px", gridArea: "main_comment_buttons"
type="submit">
reply now
</button>
</FormWrapper>
nestedComments
</CommentDisplay>
)
</>
)
return (
<div style=margin: "0 auto 90px auto">
<div style=background: "#d3f7b9">
<ul style=padding: "18px", margin: "0px 15px 0px 15px", width: "80vw">
<li> <h2>Animation using React-spring useTransition not working as expected. All the divs get animated instead of only the respective divs when the "show/hide comments" button or the "reply" buttons are clicked. </h2></li>
<ul style=margin: "10px 30px">
<li>Each top level comment is mapped into the Comment function (Line 715) </li><br/>
<li>The Comment function (Line 548) recursively loops thru each top level comment's nested replies, replies of replies, replies of replies of replies etc.... adds useTansition to each and renders them. </li><br/>
<li> When the show/hide button is clicked, it changes the boolean state for all its children to true causing the children to collapse out of view. This is the part i want to animate. Same thing when the reply button is clicked, the reply form should be animated.</li><br/>
</ul>
</ul>
</div>
<div style=position: "relative">
allComments.map( (c) =>
return (
<Comment
key=c.id
item=c
rows=rows
showMore=showMore
handleShowMoreButton=handleShowMoreButton
handleReplyButton=handleReplyButton/>
)
)
</div>
</div>
)
class App extends React.Component
render()
return (
<CommentSection/>
);
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
【问题讨论】:
【参考方案1】:我最终使用了一组 ref 来为回复表单和显示/隐藏按钮设置动画。是我所有的 setStates 导致了许多渲染并且没有让 CSS 过渡动画工作。
Link to working solution
function CommentSection(props)
//all the comments from server, usually in useeffect hook but hardcoded here instead
const artDataComments = [
"id": 295,
"body": "This is a First level comment to the main Story blah blah...",
"created_at": "2021-07-16T17:17:10.410Z",
"updated_at": "2021-07-16T17:17:10.410Z",
"original_comment_author": null,
"parent_id": null,
"ancestry": null,
"date": "less than a minute",
"comment_number": 256,
"reply": false,
"user_id": 1,
"commentable_type": "Story",
"commentable_id": 1,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "Jimmy",
"comments": [
"id": 296,
"body": "this is the first reply to the main comment ",
"created_at": "2021-07-16T17:17:49.585Z",
"updated_at": "2021-07-16T17:17:49.585Z",
"original_comment_author": "undefined",
"parent_id": 295,
"ancestry": "295",
"date": "less than a minute",
"comment_number": 257,
"reply": true,
"user_id": 1,
"commentable_type": "Comment",
"commentable_id": 295,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "izzy",
"comments": [
"id": 298,
"body": "Reply to the reply (3rd level)",
"created_at": "2021-07-16T17:22:46.088Z",
"updated_at": "2021-07-16T17:22:46.088Z",
"original_comment_author": "undefined",
"parent_id": 296,
"ancestry": "295/296",
"date": "less than a minute",
"comment_number": 259,
"reply": true,
"user_id": 1,
"commentable_type": "Comment",
"commentable_id": 296,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "Noel",
"comments": [
"id": 299,
"body": "another reply to a reply (4th level) etc...",
"created_at": "2021-07-16T17:23:10.561Z",
"updated_at": "2021-07-16T17:23:10.561Z",
"original_comment_author": "undefined",
"parent_id": 298,
"ancestry": "295/296/298",
"date": "less than a minute",
"comment_number": 260,
"reply": true,
"user_id": 1,
"commentable_type": "Comment",
"commentable_id": 298,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "Mitch",
"comments": []
]
]
,
"id": 297,
"body": "this is a second reply to the main comment .... ",
"created_at": "2021-07-16T17:18:59.249Z",
"updated_at": "2021-07-16T17:18:59.249Z",
"original_comment_author": "undefined",
"parent_id": 295,
"ancestry": "295",
"date": "less than a minute",
"comment_number": 258,
"reply": true,
"user_id": 1,
"commentable_type": "Comment",
"commentable_id": 295,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "mike",
"comments": []
]
,
"id": 294,
"body": "This is another First Level comment to the main story ... ",
"created_at": "2021-07-16T17:16:19.314Z",
"updated_at": "2021-07-16T17:16:19.314Z",
"original_comment_author": null,
"parent_id": null,
"ancestry": null,
"date": "less than a minute",
"comment_number": 255,
"reply": false,
"user_id": 1,
"commentable_type": "Story",
"commentable_id": 1,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "Natalie",
"comments": []
]
;
const allShowMoreRefs = useRef([]);
allShowMoreRefs.current = []
const allReplyRefs = useRef([]);
allReplyRefs.current = []
const getReplyArray = (childrenCommentArray) =>
let tempArray = []
childrenCommentArray.map( (x, i) =>
x.id
tempArray.push(x.id + ", ")
)
return tempArray.length > 0 ? tempArray : "blank"
const handleReplyButton = (childrenCommentArray, e, itemID) =>
allReplyRefs.current.map ( (current, i) =>
if (itemID == current.id.substr(0, current.id.indexOf('-')))
if (current.classList.contains("replyForm"))
current.classList.remove("replyForm")
else
current.classList.add("replyForm")
)
const handleShowMoreButton = (childrenCommentArray, e, itemID) =>
//set the label
if (e.target.innerText == "hide replies")
e.target.innerText = "show replies"
else
e.target.innerText = "hide replies"
//get the ref(s) and change the css
childrenCommentArray.map(item =>
allShowMoreRefs.current.map ( (current, i) =>
if (item.id == current.id)
if (current.classList.contains("shrink"))
current.classList.remove("shrink")
else
current.classList.add("shrink")
)
)
//function called recursivley depended on how many nested comments get returned from server
const Comment = ( item, userState, storyID, setArtDataComments, handleShowMoreButton, handleReplyButton) =>
const addToShowMoreRefs = (el) =>
if (el && !allShowMoreRefs.current.includes(el))
allShowMoreRefs.current.push(el)
const addToReplyRefs = (el) =>
//console.log("size b4 going in addToReplyeRefs is ", allReplyRefs.current.length )
console.log("in================= addTo_Reply_Refs")
if (el && !allReplyRefs.current.includes(el))
//console.log("inside================= addToReplyeRefs")
//console.log(el)
allReplyRefs.current.push(el)
console.log("size after adding one is ", allReplyRefs.current.length )
const nestedComments = (item.comments || []).map(com =>
return (
<div key=item.id>
<Comment style=border: "2px solid blue"
item=com type="child"
userState=userState
storyID=storyID
setArtDataComments=setArtDataComments
handleShowMoreButton=handleShowMoreButton
handleReplyButton=handleReplyButton />
</div>
)
);
return (
<CommentDisplay
ref=addToShowMoreRefs
className="replies"
key=item.id + "commentDisplay"
item=item id=item.id >
<BorderDiv/>
<TopBarWrapper>
<img src="defaultAvatar"/>
<h3 style=alignSelf: "center", fontSize: ".6em", gridArea: "nick", marginRight: "8px">
item.author_nick
</h3>
<span style=alignSelf: "center", gridArea: "date", fontSize: ".6em", color: "gray"></span>
</TopBarWrapper>
<CommentBody style=gridArea: "body", fontSize: "15px">
item.body this comment ID is item.id and its children array is getReplyArray(item.comments)
</CommentBody>
<BottomBarWrapper>
<Reply onClick=(e) => handleReplyButton(item.comments, e, item.id)>reply</Reply>
<VoteUp onClick=(e)=>handleVoteUp(e, item.id)>
<svg viewBox="0 0 22 20" xmlns="http://www.w3.org/2000/svg"><path key=item.id + "path1" data-id= item.id + "path1"
d="M10.74.04a2.013 2.013 0 00-1.58 1.88c-.11 2.795-.485 4.45-2.283
6.946a1.272 1.272 0 00-1.065-.58h-4.55C.573 8.287 0 8.84 0 9.507v8.773c0 .667.572 1.218 1.263 1.218h4.55c.435 0 .821-.22 1.049-.548.263.204.506.387.758.533.417.24.887.384 1.532.45 1.29.128 3.403.032 8.283.052a.53.53 0 00.317-.113c1.224-.667 4.255-5.775 4.248-10.534-.026-1.138-.542-1.78-1.532-1.78H13.96c.388-2.47.131-4.738-.735-6.208C12.76.555 12.078.111 11.403.018a2.035 2.035 0 00-.663.022m2.154 7.912c-.055.28.201.58.498.58h6.934c.356.035.67.091.67.913 0 1.047-.168 2.886-1.031 5.057-.865 2.172-2.155 4.531-2.603 4.455-1.215.08-7.014.109-8.108 0-.556-.056-.818-.135-1.113-.306-.266-.152-.59-.423-1.066-.791v-7.6c2.349-2.88 2.979-5.302 3.096-8.3.338-1.495 1.702-1.082 2.179-.13.697 2.402.879 4.442.544 6.122M1.263 9.262h4.55c.148 0 .251.1.251.244v8.773c0 .144-.103.243-.252.243h-4.55c-.148 0-.251-.099-.251-.243V9.506c0-.144.103-.244.252-.244"></path></svg>
<span commentid=item.id>item.total_upvotes</span>
</VoteUp>
<VoteDown onClick=(e)=>handleVoteDown(e, item.id)>
<svg viewBox="0 0 22 20" xmlns="http://www.w3.org/2000/svg"><path key=item.id + "path2" data-id= item.id + "path2" d="M11.26 19.96a2.013 2.013 0 001.58-1.881c.11-2.794.484-4.45 2.282-6.945.224.345.618.58 1.066.58h4.548c.692 0 1.264-.553 1.264-1.22V1.722c0-.668-.572-1.22-1.264-1.22h-4.548c-.436 0-.823.22-1.05.55a6.898 6.898 0 00-.759-.534c-.416-.24-.887-.384-1.531-.45C11.558-.06 9.445.037 4.564.017a.521.521 0 00-.316.114C3.023.796-.007 5.904 0 10.663c.025 1.138.541 1.78 1.532 1.78H8.04c-.39 2.47-.131 4.738.735 6.208.467.794 1.148 1.238 1.823 1.331a2.034 2.034 0 00.663-.022m-2.155-7.913c.056-.28-.202-.579-.497-.579H1.674c-.356-.035-.67-.091-.67-.913 0-1.047.166-2.886 1.031-5.057C2.9 3.326 4.19.967 4.638 1.044c1.214-.081 7.014-.109 8.108 0 .556.055.818.134 1.113.305.265.152.59.423 1.066.791v7.6c-2.349 2.88-2.979 5.302-3.096 8.3-.338 1.495-1.702 1.083-2.179.13-.697-2.402-.88-4.442-.545-6.123m11.631-1.309h-4.548c-.149 0-.252-.1-.252-.244V1.722c0-.144.103-.244.252-.244h4.548c.15 0 .253.1.253.244v8.772c0 .144-.103.244-.253.244"></path></svg>
<span commentid=item.id>item.total_downvotes</span>
</VoteDown>
<span
style=cursor: "pointer", marginLeft: "10px", fontSize: "10px", lineHeight: "40px"
onClick=(e) => handleShowMoreButton(item.comments, e, item.id)>
item.comments === undefined || item.comments.length == 0 ? "" : "hide replies"
</span>
</BottomBarWrapper>
<div
id=item.id + "-replyform"
className="replyFormHidden"
ref=props.addToReplyRefs
commentid=props.commentid
ref=addToReplyRefs
originalcommentAuthor=item.author_nick
userState=userState
storyID=storyID
commentid=item.id
setArtDataComments=setArtDataComments
handleReplyButton=handleReplyButton>
<img src="defaultManIcon"></img>
<Form
id=item.id + "form"
className="form-inline"
onSubmit="handleAdd"
enctype="multipart/form-data" >
<div style=width: "100%", height: "100%" className="field" >
<Textarea
type="textarea"
className="form-control"
index=1
placeholder="...reply to " + props.commentAuthor
name="comment"
onKeyPress=e =>
if(e.key === 'Enter')
e.preventDefault()
/>
</div>
</Form>
<button
form=item.id + "form"
style=marginTop: "3px", gridArea: "main_comment_buttons"
type="submit" >
reply now
</button>
</div>
nestedComments
</CommentDisplay>
)
return (
<Comments>
<div>
<div style=position: "relative">
artDataComments.map( (c, i) =>
return (
<div key=c.id>
<Comment
item=c
userState=props.userState
handleShowMoreButton=handleShowMoreButton
handleReplyButton=handleReplyButton />
</div>
)
)
</div>
</div>
</Comments>
);
【讨论】:
以上是关于使用 React-spring useTransition 的动画在递归循环中无法按预期工作的主要内容,如果未能解决你的问题,请参考以下文章
将 react-spring useSprings 与动态项目数组一起使用