Home > Software engineering >  React Native component not re-rendering when state is updated
React Native component not re-rendering when state is updated

Time:01-03

I have a component in my React Native app that displays a list of pending friends. This component makes a GET request to an API to retrieve the list of pending friends and then uses a useEffect hook to map over the list and render each friend as a Pressable component. I'm also using the useFocusEffect hook to make the get request when the screen renders.

Here is the relevant code for the component:

const Pending = () => {
  const [pendingFriends, setPendingFriends] = useState(null)
  let pendingFriendsRender = []

  useEffect(() => {
    if (pendingFriends !== null) {
      for(let i = 0; i < pendingFriends.length; i  ) {
        pendingFriendsRender.push(
          <Pressable  key={i} style={styles.friend}>
            <Text style={styles.friendText}>{pendingFriends[i].username}</Text>
          </Pressable>
        )
      }
    }
  }, [pendingFriends])

  useFocusEffect(
    useCallback(() => {
      async function fetchData() {
        const accessToken = await AsyncStorage.getItem('accessToken')
        try {
          const res = await instance.get('/pending_friends', {
            headers: { authorization: `Bearer ${accessToken}`},
          })
          setPendingFriends(res.data)
        } catch (error) {
          console.log(error.response.status)
        }
      }
      fetchData()
    }, [])
  )

  return(
    <View style={styles.friendsContainer}>
      {pendingFriendsRender}
    </View>
  )
}

I have tried using an empty array as the second argument in the useEffect hook but that approach has not worked. I also tried removing the useEffect hook so the if statement with the for loop stands at the top of the component without the hook, that worked but I can't update it in this way after the component rendered. I checked the API and it is returning the correct data.

CodePudding user response:

The first useEffect you have really isn't needed. You can map through your state inside of your JSX. Anytime the state changes, the component will be re-rendered:

  // Need a default here, could also set some loading state when fetching your data
  if(pendingFriends === null) {
    return <>Loading...</>
  }

  return(
    <View style={styles.friendsContainer}>
      {pendingFriends.map((friend, i) => {
          return (
            <Pressable key={friend.id} style={styles.friend}>
              <Text style={styles.friendText}>{friend.username}</Text>
            </Pressable>
          )
        })}
    </View>
  )

Also keep in mind, it's not recommended to use the index as the key, it can lead to unexpected bugs and issues. Instead use a unique string key (as shown above).

React: using index as key for items in the list

CodePudding user response:

pendingFriendsRender should be the state to:

const [pendingFriendsRender, setPendingFriendsRender] = useState([])

Instead of

let pendingFriendsRender = []

Then just clone the array so you lose reference to the object and add the new element

const newPendingFriendsRender = [...pendingFriendsRender, newElement]

or Please you can use FlatList to make it easier.

  • Related