Search code examples
react-nativeaws-amplify

How to pass data to the render method in react-native


I am trying to display something fetched via graphql in my react-native was-amplify mobile app. I can't figure out how to pass that fetched data to my render method. Here's the source code. I need to be able to show the contents of the singletour object inside render. React throws an error when I try to reference this.props.singletour inside the render method. Another thing I can't figure out is how to pass the parameter received by navigation inside render to the GetTournament graphql query. Ideally I want the id: inside GetTournament to contain navigation.getParam('itemId', 'NO-ID') and not the hardcoded id. Again, react throws an error when I access this parameter inside the async method call...ANY help would be greatly appreciated!!

 class DetailsScreen extends React.Component {

 async componentDidMount() {
  try {
   const graphqldata = await API.graphql(graphqlOperation(GetTournament, { id: "4e00bfe4-6348-47e7-9231-a8b2e722c990" }))
  console.log('graphqldata:', graphqldata)
  this.setState({ singletour: graphqldata.data.getTournament })
  console.log('singletour:', this.state.singletour)
 } catch (err) {
   console.log('error: ', err)
  }
 }
render() {
/* 2. Get the param, provide a fallback value if not available */
const { navigation } = this.props;
const itemId = navigation.getParam('itemId', 'NO-ID');
const otherParam = navigation.getParam('otherParam', 'some default value');
return (
  <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
    <Text>Details Screen</Text>
    <Text>itemId: {JSON.stringify(itemId)}</Text>
    <Text>otherParam: {JSON.stringify(otherParam)}</Text>

    <Button
      title="Go to Home"
      onPress={() => this.props.navigation.navigate('Home')}
    />
    <Button
      title="Go back"
      onPress={() => this.props.navigation.goBack()}
    />
  </View>
  );
  }
 }

Solution

  • I think I know what you are trying to do, and it can be achieved with a refactor of your code.

    This is what I would do:

    1. Capture your navigation parameters in the constructor of your component and save them to state.
    2. Set up an initial value for singleTour in state. Set a value for loaded in state. The loaded value will allow us to determine whether the data has come back successfully or not.
    3. Refactor your componentDidMount so that it uses the itemId that is now stored in the state.
    4. Refactor your console.log that checks whether you have set the state, as that is not being performed correctly.
    5. In the render pull the values from state and handle wether the data has been loaded or not. You may wish to show some loading screen or not want to handle it at all.

    Here is the refactor:

    class DetailsScreen extends React.Component {
      constructor (props) {
        super(props);
    
        // capture the values that you have passed via your navigation in the constructor
        const { navigation } = props;
        const itemId = navigation.getParam('itemId', 'NO-ID');
        const otherParam = navigation.getParam('otherParam', 'some default value');
    
        this.state = {
          itemId: itemId,
          otherParam: otherParam,
          loaded: false,
          singletour: [] // you don't state what singletour is but you should set a default value here
        };
      }
    
      async componentDidMount () {
        try {
          // we can now use the state value for itemId as we captured it in the constructor of the component
          const graphqldata = await API.graphql(graphqlOperation(GetTournament, { id: this.state.itemId }));
          console.log('graphqldata:', graphqldata);
    
          // this is a bad way to access state after it has been set,
          // state is asynchronous and takes time to set.
          // You would need to access set by using the callback method
          // this.setState({ singletour: graphqldata.data.getTournament });
          // console.log('singletour:', this.state.singletour); // <- you should never do this after you setState
    
          // this is how you should access state after you have set it
          // this will guarantee that the state has been set before the
          // console.log is called, so it should show the correct value of state
          this.setState({
            singletour: graphqldata.data.getTournament,
            loaded: true // we are going to use the loaded value to handle our render
          }, () => console.log('singletour:', this.state.singletour));
        } catch (err) {
          console.log('error: ', err);
          // you may want to show an error message on the screen.
        }
      }
      render () {
        // access the passed parameters from state
        const { itemId, otherParam, loaded, singletour } = this.state;
    
        if (loaded) {
          // if once the data is loaded we can show screen
          return (
            <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
              <Text>Details Screen</Text>
              <Text>itemId: {JSON.stringify(itemId)}</Text>
              <Text>otherParam: {JSON.stringify(otherParam)}</Text>
              <Text>singletour: {JSON.stringify(singletour)}</Text>
    
              <Button
                title="Go to Home"
                onPress={() => this.props.navigation.navigate('Home')}
              />
              <Button
                title="Go back"
                onPress={() => this.props.navigation.goBack()}
              />
            </View>
          );
        } else {
          // while we are waiting for the data to load we could show a placeholder screen
          // or we could show null. The choice is yours.
          return (
            <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
              <Text>Data not loaded</Text>
            </View>
          );
        }
      }
    }
    

    Note that componentDidMount get called after the first render occurs, this is why we have the loaded value in state. By using loaded it allows us to handle what is presented to the user rather than presenting a screen where the data hasn't finished loading.

    This is clearly one possible refactor of your code. There are many other ways that it could be refactored.

    Here are some great articles on setting state