简体   繁体   中英

How to re-render a tab in React-Native

I have a React-Native flashcard app that boots with two tabs, a Home tab, and a New Deck tab. The Home tab is the default, and you can press or swipe over to the New Deck tab.

  • The Home tab displays all of the decks the user currently has saved.

  • On the New Deck tab, I have the user enter the title of their new deck and press a submit button. When that submit button is pressed, I re-navigate to the Home tab.

My issue is : How in the world do I trigger a re-render on the Home tab from a button press on the New Deck tab so the user can see the deck they just created?

I know I could use Redux to solve this issue, but no other part of the app is optimized in a "Redux" fashion, and I'd really like to not redesign the architecture of my app for the sole purpose of updating a single screen, mostly because this is the only instance where I would need this ability.

I've attempted to get around this by passing screenProps containing the this.forceUpdate method all the way from the StackNavigator component, but it didn't work. I also tried manually update the state of the App component to trigger a re-render, but the re-render never happened (although the state did update).

App.js

import React, { Component } from 'react'
import { Text, View } from 'react-native'
import AlphaHome from './Components/Home/AlphaHome'
import AlphaQuiz from './Components/Quiz/AlphaQuiz'
import AlphaNewUdaciDeck from './Components/NewUdaciDeck/AlphaNewUdaciDeck'
import AlphaNewUdaciCard from './Components/NewUdaciCard/AlphaNewUdaciCard'
import AlphaUdaciDeckDetails from './Components/UdaciDeckDetails/AlphaUdaciDeckDetails'
import { TabNavigator, StackNavigator } from 'react-navigation'

const Tabs = TabNavigator({
  Home: {
    screen: AlphaHome,
    navigationOptions: {
      tabBarLabel: 'Home',
    },
  },
  NewDeck: {
    screen: AlphaNewUdaciDeck,
    navigationOptions: {
      tabBarLabel: 'New Deck',
    }
  }
}, {
  navigationOptions: {
    header: null,
  },
  tabBarOptions: {
    activeTintColor: 'white',
    indicatorStyle: {
      backgroundColor: 'white'
    },
    style: {
      height: 50,
      borderBottomColor: 'white',
      backgroundColor: 'deepskyblue',
    }
  },
})

const Stack = StackNavigator({
  Home: {
    screen: Tabs,
  },
  AlphaNewUdaciDeck: {
    screen: AlphaNewUdaciDeck,
    navigationOptions: {
      headerTintColor: 'white',
      headerStyle: {
        backgroundColor: 'deepskyblue'
      }
    }
  },
  AlphaNewUdaciCard: {
    screen: AlphaNewUdaciCard,
    navigationOptions: {
      headerTintColor: 'white',
      headerStyle: {
        backgroundColor: 'deepskyblue'
      }
    }
  },
  AlphaUdaciDeckDetails: {
    screen: AlphaUdaciDeckDetails,
    navigationOptions: {
      headerTintColor: 'white',
      headerStyle: {
        backgroundColor: 'deepskyblue'
      }
    }
  },
})

export default class App extends Component {
  render() {
    return (
      <Stack />
    )
  }
}

Home.js

import React, { Component } from 'react'
import { ScrollView, View, Text, StyleSheet, AsyncStorage, ActivityIndicator } from 'react-native'
import UdaciDeck from '../Reusable/UdaciDeck'
import { getAllData } from '../../utils/AsyncApi'

export default class HomeExistingUser extends Component {
  state = {
    decks: null,
  }
  componentDidMount() {
    let decks = getAllData()
    setTimeout(() => {
      this.setState({
        decks
      })
    }, 1000)
  }
  showDetails = (title, count) => {
    this.props.navigation.navigate('AlphaUdaciDeckDetails', {title, count})
  }
  render() {
    const {decks} = this.state
    return (
      decks
      ? <ScrollView contentContainerStyle={styles.container}>
            {decks.map(s => <UdaciDeck key={s[1].title} name={s[1].title} count={s[1].questions.length} method={this.showDetails} />)}
        </ScrollView>
      : <View style={[styles.container, {flex: 1, justifyContent: 'center'}]}>
          <ActivityIndicator size='large' color='white' />
        </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    minHeight: '100%',
    backgroundColor: 'lightskyblue',
    paddingTop: 20,
    paddingBottom: 20,
    alignItems: 'center',
  },
})

NewDeck.js

import React, { Component } from 'react'
import { View, Text, TextInput, StyleSheet, AsyncStorage, TouchableNativeFeedback, Alert } from 'react-native'
import { addDeck } from '../../utils/AsyncApi'

// BUG: when adding a new deck (if HomeExistingUser is true) view doesn't update. Need to figure out a way to update on tab navigate back

export default class AlphaNewUdaciDeck extends Component {
  state = {
    input: '',
    keys: null,
  }
  componentDidMount() {
    AsyncStorage.getAllKeys()
      .then(keys => this.setState({
        keys
      }))
  }
  handleSubmit = () => {
    const {input, keys} = this.state
    input.search(' ') > 0 || input.length < 1 || keys.filter(s => s === input).length > 0
      ? Alert.alert(`Please enter a valid name (${input.length < 1 || keys.filter(s => s === input).length > 0 ? `you can't save a deck with ${input.length < 1 ? 'no' : 'an already used'} name` : "no spaces"})`)
      : addDeck(input)
    ;if(input.search(' ') < 0 || input.length > 0 || keys.filter(s => s === input).length < 1) {
      this.props.navigation.goBack()
    }
  }
  render() {
    return (
      <View style={[styles.container, styles.containerOne]}>
         <View style={styles.containerTwo}>
            <Text style={styles.text}>Name of the deck</Text>
            <Text style={styles.text}>(Please no spaces)</Text>
            <TextInput
              autoFocus={true}
              onChangeText={(input) => this.setState({
                input
              })}
              selectionColor={'deepskyblue'}
              underlineColorAndroid={'transparent'}
              style={styles.input}
            />
            <TouchableNativeFeedback onPress={this.handleSubmit}>
              <View style={styles.btn}>
                <Text style={styles.btnText}>Save Deck</Text>
              </View>
            </TouchableNativeFeedback>
          </View>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'lightskyblue',
  },
  containerOne: {
    alignItems: 'center',
  },
  containerTwo: {
    marginTop: 50,
  },
  text: {
    color: 'white',
    fontSize: 20,
  },
  input: {
    backgroundColor: 'white',
    height: 50,
    width: 300,
    marginTop: 15,
    fontSize: 20,
    paddingLeft: 5,
    paddingRight: 5,
    color: 'deepskyblue'
  },
  btn: {
    backgroundColor: 'deepskyblue',
    marginTop: 50,
    padding: 20,
    paddingLeft: 50,
    paddingRight: 50,
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 5,
  },
  btnText: {
    color: 'white',
  },
})

You should check out react-navigation-is-focused-hoc at https://github.com/pmachowski/react-navigation-is-focused-hoc to solve the specific problem you mentioned.

You can also try

onNavigationStateChange(prevState, newState) 

there is a sample at How can I tell if the screen is navigated to with ReactNavigation

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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