Case is that I have three screens, that display results fetched from an API and allows the user to dispatch actions on those results. Those actions trigger (should) results in the other two screens. In other words, if the user is on any screen and performs some action, the other two screens should update.
For instance, Screens A, B and C. I can do one of the two following approaches:
- Conditional Render:
class MainScreen extends Component {
state: Object;
constructor(props) {
super(props);
this.state = { currentActiveScreen: 1 }
}
componentWillMount()
{
this.retrieveResultForScreenA();
this.retrieveResultForScreenB();
this.retrieveResultForScreenC();
}
retrieveResultForScreenA()
{
// get results from API
}
retrieveResultForScreenB()
{
// get results from API
}
retrieveResultForScreenC()
{
// get results from API
}
ChangeScreen(screen_number)
{
this.setState({currentActiveScreen: screen_number});
}
render()
{
if(this.state.currentActiveScreen === 1)
{
// render screen A results
// along with a tab bar to switch screens:
<View style={{flexDirection: "row"}}>
<TouchableOpacity onPress={()=>{ this.ChangeScreen.bind(this, 1) }}>
<Text>ScreenA</Text>
</TouchableOpacity>
<TouchableOpacity onPress={()=>{ this.ChangeScreen.bind(this, 2) }}>
<Text>ScreenB</Text>
</TouchableOpacity>
<TouchableOpacity onPress={()=>{ this.ChangeScreen.bind(this, 3) }}>
<Text>ScreenC</Text>
</TouchableOpacity>
</View>
}
if(this.state.currentActiveScreen === 2)
{
// render screen B results
// along with a tab bar to switch screens:
<View style={{flexDirection: "row"}}>
<TouchableOpacity onPress={()=>{ this.ChangeScreen.bind(this, 1) }}>
<Text>ScreenA</Text>
</TouchableOpacity>
<TouchableOpacity onPress={()=>{ this.ChangeScreen.bind(this, 2) }}>
<Text>ScreenB</Text>
</TouchableOpacity>
<TouchableOpacity onPress={()=>{ this.ChangeScreen.bind(this, 3) }}>
<Text>ScreenC</Text>
</TouchableOpacity>
</View>
}
if(this.state.currentActiveScreen === 3)
{
// render screen C results
// along with a tab bar to switch screens:
<View style={{flexDirection: "row"}}>
<TouchableOpacity onPress={()=>{ this.ChangeScreen.bind(this, 1) }}>
<Text>ScreenA</Text>
</TouchableOpacity>
<TouchableOpacity onPress={()=>{ this.ChangeScreen.bind(this, 2) }}>
<Text>ScreenA</Text>
</TouchableOpacity>
<TouchableOpacity onPress={()=>{ this.ChangeScreen.bind(this, 3) }}>
<Text>ScreenA</Text>
</TouchableOpacity>
</View>
}
}
}
- TabNavigator With Three Screens:
class ScreenA extends Component {
static navigationOptions = ({ navigation }) => ({ title: 'ScreenA' });
constructor(props) {
super(props);
}
componentWillMount()
{
this.retrieveResultForScreenA();
}
retrieveResultForScreenA()
{
// get results from API
}
render() {
return (
// render screen A results
);
}
}
class ScreenB extends Component {
static navigationOptions = ({ navigation }) => ({ title: 'ScreenB' });
constructor(props) {
super(props);
}
componentWillMount()
{
this.retrieveResultForScreenB();
}
retrieveResultForScreenA()
{
// get results from API
}
render() {
return (
// render screen B results
);
}
}
class ScreenC extends Component {
static navigationOptions = ({ navigation }) => ({ title: 'ScreenC' });
constructor(props) {
super(props);
}
componentWillMount()
{
this.retrieveResultForScreenC();
}
retrieveResultForScreenA()
{
// get results from API
}
render() {
return (
// render screen C results
);
}
}
const MainScreen = TabNavigator({
ScreenA: { screen: MyScreenA },
ScreenB: { screen: MyScreenB },
ScreenC: { screen: MyScreenC },
});
Problem with the first approach is that:
Problem with the second approach is that:
How can I combine both approaches and have clean code, with up to date screens?
In response to the discussion happening in the comments;
It seems like what you really want is a handler function that can trigger updates on specific user actions. This fits, somewhat, into your "Conditional Render" design pattern. I'll give an example, but extremely simplified;
class MainScreen extends Component {
state: Object;
constructor(props) {
super(props);
this.state = { currentActiveScreen: 1 }
}
componentWillMount() {
this.handleFetchRequest();
}
getTabSelection() {
return (
//some JSX with links that controls `state.currentActiveScreen`
);
}
handleFetchRequest() {
this.retrieveResultForScreenA();
this.retrieveResultForScreenB();
this.retrieveResultForScreenC();
}
getCurrentScreen() {
if(this.state.currentActiveScreen === 1) {
return <ScreenA onFetchRequest={this.handleFetchRequest}/>;
}
if(this.state.currentActiveScreen === 2) {
return <ScreenB onFetchRequest={this.handleFetchRequest}/>;
}
if(this.state.currentActiveScreen === 3) {
return <ScreenC onFetchRequest={this.handleFetchRequest}/>;
}
}
render() {
return <div>
{this.getTabSelection()}
{this.getCurrentScreen()}
</div>;
}
}
class ScreenA extends Component {
render() {
return <button onClick={this.props.onFetchRequest}/>;
}
}
So in the above example, the component will call handleFetchRequest
once when the component first mounts, and then will call additionally when the user clicks the button rendered within ScreenA
. Any other updates or re-renders of the components will not cause a re-fetch.
You can continue to extend this to other user actions that should trigger a refetch, such as onFocus
or onBlur
of input fields.