Wes
Wes

Reputation: 1945

How do I pass a value from a promise to a component prop in react native?

Edit: I don't understand the reason for downvotes, this was a good question and no other questions on this site solved my issue. I simply preloaded the data to solve my issue but that still doesn't solve the problem without using functional components.

I'm trying to pass users last message into the ListItem subtitle prop but I can't seem to find a way to return the value from the promise/then call. It's returning a promise instead of the value which gives me a "failed prop type". I thought about using a state but then I don't think I could call the function inside the ListItem component anymore.

  getMsg = id => {
    const m = fireStoreDB
      .getUserLastMessage(fireStoreDB.getUID, id)
      .then(msg => {
        return msg;
      });
    return m;
  };

  renderItem = ({ item }) => (
    <ListItem
      onPress={() => {
        this.props.navigation.navigate('Chat', {
          userTo: item.id,
          UserToUsername: item.username
        });
      }}
      title={item.username}
      subtitle={this.getMsg(item.id)} // failed prop type
      bottomDivider
      chevron
    />
  );

Upvotes: 0

Views: 9899

Answers (3)

Wes
Wes

Reputation: 1945

I fixed the issue by using that promise method inside another promise method that I had on componentDidMount and added user's last message as an extra field for all users. That way I have all users info in one state to populate the ListItem.

  componentDidMount() {
    fireStoreDB
      .getAllUsersExceptCurrent()
      .then(users =>
        Promise.all(
          users.map(({ id, username }) =>
            fireStoreDB
              .getUserLastMessage(fireStoreDB.getUID, id)
              .then(message => ({ id, username, message }))
          )
        )
      )
      .then(usersInfo => {
        this.setState({ usersInfo });
      });
  }

  renderItem = ({ item }) => (
    <ListItem
      onPress={() => {
        this.props.navigation.navigate('Chat', {
          userTo: item.id,
          UserToUsername: item.username
        });
      }}
      title={item.username}
      subtitle={item.message}
      bottomDivider
      chevron
    />
  );

Upvotes: 0

tobiasfried
tobiasfried

Reputation: 1842

If the 'last message' is something specific to only the ListItem component and not something you have on hand already, you might want to let the list item make the network request on its own. I would move the function inside ListItem. You'll need to set up some state to hold this value and possibly do some conditional rendering. Then you'll need to call this function when the component is mounted. I'm assuming you're using functional components, so useEffect() should help you out here:

//put this is a library of custom hooks you may want to use
//  this in other places
const useIsMounted = () => {
  const isMounted = useRef(false);
  useEffect(() => {
    isMounted.current = true;
    return () => (isMounted.current = false);
  }, []);
  return isMounted;
};

const ListItem = ({
  title,
  bottomDivider,
  chevron,
  onPress,
  id, //hae to pass id to ListItem
}) => {
  const [lastMessage, setLastMessage] = useState(null);
  const isMounted = useIsMounted();
  React.useEffect(() => {
    async function get() {
      const m = await fireStoreDB.getUserLastMessage(
        fireStoreDB.getUID,
        id
      );
      //before setting state check if component is still mounted
      if (isMounted.current) {
        setLastMessage(m);
      }
    }
    get();
  }, [id, isMounted]);

  return lastMessage ? <Text>DO SOMETHING</Text> : null;
};

Upvotes: 0

T.J. Crowder
T.J. Crowder

Reputation: 1074168

You could only do it that way if ListItem expected to see a promise for its subtitle property, which I'm guessing it doesn't. ;-) (Guessing because I haven't played with React Native yet. React, but not React Native.)

Instead, the component will need to have two states:

  • The subtitle isn't loaded yet
  • The subtitle is loaded

...and render each of those states. If you don't want the component to have state, then you need to handle the async query in the parent component and only render this component when you have the information it needs.

Upvotes: 4

Related Questions