代码之家  ›  专栏  ›  技术社区  ›  Sharon

我如何防止这种无休止的重新渲染?

  •  0
  • Sharon  · 技术社区  · 4 年前

    我正在构建一个React应用程序,但我还没有完全理解钩子的使用。

    我的数据以以下格式存储在Firestore中:

    userlists > key1 > users: [user1, user2, user3]
                     > date_created: date
                     > listname: "Work Colleagues"
              > key2 > users: [user1, user4, user5]
                     > date_created: date
                     > listname: "Badminton Friends"
    

    (其中user1、user2等是保存用户数据的对象)

    所以,在 EditUserlistPage 我想取回 userlist 带钥匙 key1 ,并渲染a UserList 组件来显示它 用户列表 然后,组件渲染一个个体 UserItem 每个用户的组件

    编辑用户列表页面 如下所示( key 通过道具传递):

    const EditUserlistPage = (props) => {
    
      // Get list key
      const listKey = props.key
    
      // Set up state variables
      const [userlist, setUserlist] = useState({})
    
      // Load list from db once component has mounted
      useEffect(() => {
        props.firebase.getListByKey(listKey).get().then(doc => {
            let userlist = doc.data()
            setUserList(userList)
        })
      }, [listKey, props.firebase]) 
    
      return (
        <div>
          <h1>Edit User List</h1>
          <UserList
            userlist={userList}
          />
        </div>
      )
    }
    
    export default withFirebase(EditUserlistPage)
    

    这个 用户列表 组件是:

    const UserList = (props) => {
    
      // Get list
      const { userlist } = props
      
      // Set up state variable - users
      const [ users,  setUsers] = useState([])
    
      // Now get all users as objects
      let usersTemp = []
    
      for(let ii=0; ii<userlist.users.count; ii++) {
    
          const user = userlist.users[ii]
          
          const userItem = {
            id: user.index,
            name: user.firstname + user.surname
            ... // More things go here, but I don't think they're relevant
          }
          usersTemp.push(userItem)
        }
      }
    
      setUsers(usersTemp)
    
      return (
        <div className="userList">
          { // This will render a UserItem component}
        </div>
      )
    
    }
    
    export default UserList
    

    最后, props.firebase.getListByKey 是:

    getListByKey = (key) => this.firestore.collection('userlists').doc(key)
    

    我收到一个错误和警告。

    首先,屏幕上显示的是: Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. 在控制台中,我也可以看到这个错误,它说:

    The above error occurred in the <UserList> component:
        in UserList (at EditUserlistPage/index.js:59)
        in div (at EditUserlistPage/index.js:54)
        in EditUserlistPage (at context.js:7)
    

    如果我在EditUserlistPage中注释掉呈现UserList组件的行,此错误就会消失。

    其次,我在控制台上收到警告:

    Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
        in EditUserlistPage (at context.js:7)
    

    context.js是Firebase上下文,它是:

    const FirebaseContext = React.createContext(null)
    
    export const withFirebase = Component => props => (
      <FirebaseContext.Consumer>
        {firebase => <Component {...props} firebase={firebase} />} // Line 7
      </FirebaseContext.Consumer>
    )
     
    export default FirebaseContext
    

    我试着阅读了关于Hooks的React文档,我发现如果实现不当,useEffect可能会导致无限的重新渲染,但我不知道如何正确地做到这一点。

    2 回复  |  直到 4 年前
        1
  •  2
  •   Gabriele Petrioli    4 年前

    主要问题在于 setUsers(usersTemp) 在代码中 UserList .

    每当某些局部状态发生变化时,组件都会重新渲染。所以,既然你总是重新设置 users 在渲染过程中,您会触发另一个渲染。

    你可以使用 useEffect 并且只更新 用户 userList 变化

    const UserList = (props) => {
    
      // Get list
      const {
        userlist
      } = props
    
      // Set up state variable - users
      const [users, setUsers] = useState([])
    
      useEffect(() => {
          // Now get all users as objects
          let usersTemp = []
    
          for (let ii = 0; ii < userlist.users.count; ii++) {
            const user = userlist.users[ii];
            const userItem = {
              id: user.index,
              name: user.firstname + user.surname
                ... // More things go here, but I don't think they're relevant
            }
            usersTemp.push(userItem)
          }
        }
    
        setUsers(usersTemp)
      }, [userlist])
    
    return (
        <div className="userList">
          { // This will render a UserItem component}
        </div>
      )
    }
    
    export default UserList
    
        2
  •  1
  •   Thales Kenne    4 年前

    当你使用功能组件时,当你的状态更新时,你的整个功能将再次运行。因此,在UserList组件中,每次渲染时都会更新状态,从而触发新的渲染。

    为了防止这种情况,请使用不依赖的useEffect。这将导致useEffect在组件挂载时只运行一次,非常像componentDidMount。

    const UserList = (props) => {
    
      // Get list
      const { userlist } = props
      
      // Set up state variable - users
      const [ users,  setUsers] = useState([])
    
      useEffect(() => {
        // Now get all users as objects
        let usersTemp = []
    
        for(let ii=0; ii<userlist.users.count; ii++) {
    
            const user = userlist.users[ii]
          
            const userItem = {
              id: user.index,
              name: user.firstname + user.surname
              ... // More things go here, but I don't think they're relevant
            }
            usersTemp.push(userItem)
          }
        }
    
        setUsers(usersTemp)
      },[])
    
    
    
      return (
        <div className="userList">
          { // This will render a UserItem component}
        </div>
      )
    
    }
    
    export default UserList
    
    推荐文章