javascript - React native - open multiple modals one after the other inside for loop - Stack Overflow

I first make an Ajax call (to an API) which provides me some data, a list of achievements (array of obj

I first make an Ajax call (to an API) which provides me some data, a list of achievements (array of objects). I would like to loop through this array, show the first achievement as a Modal and on click of a button close the modal then show the next one (next achievement) and so on.

Ajax call providing the data:

getAchievements = () => {
    fetch(url + '/achievements', {
      method: 'get',
      headers: {
        Accept: 'application/json',
        'Content-type': 'application/json'
      }
    })
    .then((data) => data.json())
    .then((data) => {
      this.props.addData({
        achievements: data.achievements
      })

      if(this.props.store.achievements.length > 0) {
        this.setState({
          showAchievementModal: true
        })
      }
    })
    .catch((error) => {
      console.error(error)
    })
}

Here I show the modals:

render() {
    return (
        {this.state.showAchievementModal &&
          <Modal
            animationType={'fade'}
            visible={this.props.store.isModalAchievementVisible}
            >
            {this.props.store.achievements.map((data,index)=>{
                return(
                    <View key={index}>
                        <View style={styles.container}>
                            <Text>{data.title}</Text>
                            <Text>{data.description}</Text>

                            <TouchableOpacity onPress={this.closeModal}>
                                <Text>Collect</Text>
                            </TouchableOpacity>
                        </View>
                  </View>
                )
            })}
          </Modal>
        }
    )
}

At the moment all the Modals open at the same time. How could I open them one after the other after clicking the Collect button?

I first make an Ajax call (to an API) which provides me some data, a list of achievements (array of objects). I would like to loop through this array, show the first achievement as a Modal and on click of a button close the modal then show the next one (next achievement) and so on.

Ajax call providing the data:

getAchievements = () => {
    fetch(url + '/achievements', {
      method: 'get',
      headers: {
        Accept: 'application/json',
        'Content-type': 'application/json'
      }
    })
    .then((data) => data.json())
    .then((data) => {
      this.props.addData({
        achievements: data.achievements
      })

      if(this.props.store.achievements.length > 0) {
        this.setState({
          showAchievementModal: true
        })
      }
    })
    .catch((error) => {
      console.error(error)
    })
}

Here I show the modals:

render() {
    return (
        {this.state.showAchievementModal &&
          <Modal
            animationType={'fade'}
            visible={this.props.store.isModalAchievementVisible}
            >
            {this.props.store.achievements.map((data,index)=>{
                return(
                    <View key={index}>
                        <View style={styles.container}>
                            <Text>{data.title}</Text>
                            <Text>{data.description}</Text>

                            <TouchableOpacity onPress={this.closeModal}>
                                <Text>Collect</Text>
                            </TouchableOpacity>
                        </View>
                  </View>
                )
            })}
          </Modal>
        }
    )
}

At the moment all the Modals open at the same time. How could I open them one after the other after clicking the Collect button?

Share Improve this question asked Mar 9, 2019 at 14:35 JohnJohn 3,79914 gold badges44 silver badges49 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 2

This is the updated version of my code that works:

Initialising activeModalIndex in the constructor:

constructor(props) {
  super(props)

  this.state = {
    activeModalIndex: 0
  }
}

Get achievements:

getAchievements = () => {
  if(this.props.store.achievements.length > 0) {
    this.setState({
      showAchievementModal: true,
      activeModalIndex: 0,
    })
  }
}

Render function:

 render() {
   return this.props.store.achievements.map((data,index) => this.state.activeModalIndex === index &&
     <Modal>
       <View key={index}>
         <View style={styles.container}>
           <Text>{data.title}</Text>
           <Text>{data.description}</Text>

           <TouchableOpacity onPress={this.closeModal}>
             <Text>Collect</Text>
           </TouchableOpacity>
        </View>
      </View>
    </Modal>
  )
}

Close Modal:

closeModal = () => {
  const maxIndex = this.props.store.achievements.length - 1
  const currentIndex = this.state.activeModalIndex
  const isLastModal = currentIndex === maxIndex
  const newIndex = isLastModal? -1: currentIndex +1

  this.setState({
    activeModalIndex: newIndex
  })
}

The problem is that you have multiple Modals on your page and they all use the same boolean to check if they should be rendered. Initially, showAchievementModal is set to true, so all modals are rendered. Furthermore, after you set showAchievementModal to false in closeModal, it will permanently stay false, so no additional modals will get rendered.

render() {
    return (
        {this.state.showAchievementModal &&
          <Modal
             ...          
          </Modal>
        }
    )
}

Instead of showAchievementModal you should be keeping track of index of active modal. So, after you fetch the list of achievements from your API, set the activeModalIndex to 0. After the user dismisses this first modal, set the activeModalIndex to 1 inside the closeModal method, then set it to 2 after the second modal is closed and so on.

Now, for every modal to correspond to a single achievement, we must map each element of the achievements array to a single Modal and conditionally render it only if its corresponding index is the active one.

render() {
    const achievements = this.props.store.achievements;
    const { activeModalIndex } = this.state;

    return achievements.map((data, index) => activeModalIndex === index &&
      <Modal key={index}>
        <View>
          <View style={styles.container}>
            <Text>{data.title}</Text>
            <Text>{data.description}</Text>

            <TouchableOpacity onPress={this.closeModal}>
              <Text>Collect</Text>
            </TouchableOpacity>
          </View>
        </View>
      </Modal>
    )
}

When the users dismisses the currently active modal, simply increment the index of the active modal and the next modal will appear instead of the current one. If the new incremented value is equal or larger than the array length, nothing will get rendered, so no need to check for max index value before setting new state.

closeModal = () => {   
    this.setState(previousState => ({
      activeModalIndex: previousState.activeModalIndex + 1,
    }))
}

Also, please read about the dangers of setting index as key when rendering lists. If you happen to need ordering achievements by some value/priority and users can retrieve multiple pages of their achievements, it might cause rendering wrong ponents.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745138462a4613315.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信