使用 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 与动态项目数组一起使用

react-spring 插值函数不适用于打字稿

如何反向运行 react-spring 动画?

将 react-spring 用于 react-native

react-spring 中的“动画”做啥?

React-Spring useTransition 将我的列表项打乱