Yakult
Yakult

Reputation: 565

React Navigation: Share state between two screens within TabNavigator

I'm learning react native by building a simple chat app. I have two screens wrapped in a TabNavigator where the first screen (Screen A) being the chatbox, and the other screen (Screen B) which displays a list of online users. I'm using SocketIO to fetch these users.

Problem is, how can I access the "onlineUsers" state from ScreenA to ScreenB so I can see an updated list of online users whenever I receive a "user joins" event?

Screen A:

export default class ScreenA extends Component {
  constructor(props) {
    super(props);

    this.state = {
      onlineUsers = [];
    }
  }

  componentDidMount() {
    // Update list of online users when new user joins chat
    this.socket.on('user joins', (payload) => {
      this.setState({
        onlineUsers: payload.users
      })
    })
  }
}

Screen B:

export default class ScreenB extends Component {
  constructor(props) {
    super(props);

    // I want to get the onlineUsers from ScreenA
    this.state = {
      onlineUsers = [];
    }
  }
}

Router:

export const Chat = TabNavigator({
  ChatBox: {
   screen: ScreenA
  },
  OnlineUsers: {
   screen: ScreenB
  },
})

PS: I'm using react-navigation to handle navigation

Upvotes: 4

Views: 3352

Answers (3)

Ben
Ben

Reputation: 5626

I came across this post when I ran into a similar issue earlier this year so I thought I'd post my solution.

I would say your best bet in this situation is to use a Custom Navigator to wrap your TabNavigator which will expose <TabNavigator /> in your custom navigator allowing you to pass any methods or state down to ScreenA and ScreenB as screenProps.

The custom navigator would look like:

import React from 'react'
import { ChatTabNavigator } from './ChatTabNavigator'

class StateManagingCustomNavigator extends React.Component {
  static router = ChatTabNavigator.router

  state = {
    onlineStatus: [theStatus]
  }

  handleMessagePosted = () => {
    // Handle message post, etc...
  }

  // ... SocketIO code and other logic to manage the state here? ...

  render() {
    const { navigation } = this.props
    const { onlineStatus } = this.state

    return (
      <ChatTabNavigator
       screenProps={{
         theStatus: onlineStatus,
         chatMessagePosted: this.handleMessagePosted
       }}
       navigation={navigation}
      />
    )
  }
}

export default StateManagingCustomNavigator

From here you could implement an event system as @Ashish Prakash suggested, or manage all of your state in the custom navigator and transform ScreenA and ScreenB into presentational components.

Upvotes: 0

Bright Lee
Bright Lee

Reputation: 2326

Use this.props.navigation.setParam({onlineUsers: onlineUsers: payload.users}) when you get user-list from server.

Then use it in Screen B like this.props.navigation.state.params.onlineUsers

Upvotes: -1

Ashish Prakash
Ashish Prakash

Reputation: 235

Best way is to handle events in the parent component and then passing it to their children components. So in your case, you should have a online user list in your router. Then pass the array to screen B. Here is how you should do

Router

state = {
    online_users:[]
}

_update = (data) => {
    this.setState({online_users:data});
};

export const Chat = TabNavigator({
   ChatBox: {
       screen: <ScreenA onUpdate={this._update}/>
    },
    OnlineUsers: {
       screen: <ScreenB userList={this.state.online_users}>
    },
})

Screen A

export default class ScreenA extends Component {
    constructor(props) {
        super(props);
    }

    componentDidMount() {
        // Update list of online users when new user joins chat
        this.socket.on('user joins', (payload) => {
            this.props.onUpdate(payload.users)
        })
    }
}

Screen B

export default class ScreenB extends Component {
    constructor(props) {
        super(props);
    }

    // You can access online user using this.props.userList

 }

Upvotes: 3

Related Questions