简体   繁体   English

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

[英]How do I prevent this endless re-render in react?

I'm building a React app, but I haven't quite got my head around the use of hooks yet.我正在构建一个 React 应用程序,但我还没有完全了解钩子的使用。

My data is stored in the following format in Firestore:我的数据以以下格式存储在 Firestore 中:

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

(where user1, user2 etc are objects holding user data) (其中 user1、user2 等是保存用户数据的对象)

So, in EditUserlistPage I want to retrieve the userlist with key key1 , and render a UserList component to show it.所以,在EditUserlistPage我要检索的userlist与主要key1 ,并呈现UserList组件来证明这一点。 The UserList component then renders an individual UserItem component for each user UserList组件然后为每个用户呈现一个单独的UserItem组件

EditUserlistPage is as follows ( key is passed in via props): EditUserlistPage如下( key通过props传入):

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)

The UserList component is: UserList组件是:

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

Finally, props.firebase.getListByKey is:最后, props.firebase.getListByKey是:

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

I'm getting an error and a warning.我收到错误和警告。

Firstly, displayed on screen is: Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.首先,屏幕上显示的是: Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. In the console I can also see this error, and it says:在控制台中,我也可以看到这个错误,它说:

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)

This error goes away if I comment out the line in EditUserlistPage which renders the UserList component.如果我注释掉 EditUserlistPage 中呈现 UserList 组件的行,这个错误就会消失。

Secondly, I'm getting a warning in the console:其次,我在控制台中收到警告:

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 is the Firebase Context, and is: 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

I've tried to read the React documentation on Hooks, and I've gathered that useEffect can caused infinite re-rendering if not implemented properly, but I can't figure out how to do it correctly.我尝试阅读有关 Hooks 的 React 文档,并且我发现如果未正确实现 useEffect 会导致无限重新渲染,但我不知道如何正确执行。

The main problem is with setUsers(usersTemp) in the code of the UserList .主要问题在于UserList代码中的setUsers(usersTemp)

Whenever some local state is changed, the component re-renders.每当某些本地状态发生更改时,组件就会重新渲染。 So, since you always re-set the users during the render, you trigger another render.因此,由于您总是在渲染期间重新设置users ,因此您触发了另一个渲染。

You could use a useEffect and only update the users when the userList changes您可以使用useEffect并仅在userList更改时更新users

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

When you're using functional components, your entire function will run again when your state updates.当您使用函数式组件时,当您的状态更新时,您的整个函数将再次运行。 So in your UserList component, you're updating the state every time it renders, therefore triggering a new render.因此,在您的 UserList 组件中,每次渲染时都会更新状态,从而触发新的渲染。

To prevent that, use useEffect with no dependencies.为了防止这种情况,请使用没有依赖项的 useEffect。 That will cause the useEffect to only run once when the component mounts, very much like a componentDidMount.这将导致 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM