最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to open Modal with item information when clicking a FlatList element? - Stack Overflow

programmeradmin1浏览0评论

What I am trying to achieve is to open a modal dialog when the user press on one of the items of a FlatList ponent. The code of this list looks like this:

class MyItem extends React.Component { 
  _onPress = () => { 
    this.props.onPressItem(this.props.item); 
  }; 
  render() { 
    return(
        <TouchableOpacity 
            {...this.props}
            onPress={this._onPress}
            >
            <Text style={styles.itemText}> {this.props.item.name}</Text>
        </TouchableOpacity>
    ) 
  } 
}

export default class MyList extends React.PureComponent {
    constructor(props) {
        super(props);
        this.state = {
            data: {}, // some data correctly loaded
            isModalVisible: false
        };
    };

    _onPressItem = (item) => { 
        this._showModal;
    };

    _showModal = () => this.setState({ isModalVisible: true })

    _keyExtractor = (item, index) => item.id;

    _renderItem = ({item}) => (
        <MyItem 
            style={styles.row}
            item={item}
            onPressItem={this._onPressItem}
        />
    );

    render() {
      return(
        <KeyboardAvoidingView behavior="padding" style={styles.container}>
          <View style={styles.titleContainer}>
              <Text style={styles.title}>Tittle</Text>
          </View>
          <ScrollView style={styles.container}>
              <FlatList 
                  data={this.state.data}
                  ItemSeparatorComponent = {this._flatListItemSeparator}
                  renderItem={this._renderItem}
                  keyExtractor={this._keyExtractor}
              />
          </ScrollView>
          <MyModal modalVisible={this.state.isModalVisible}/>
        </KeyboardAvoidingView>
      );
    }
}

Styles, FlatList data and some functions have been removed because they are not relevant for this issue.

As you can see, MyModal ponent is declared after ScrollView ponent. The code is based on the use of react-native Modal ponent:

export default class MyModal extends Component {
  constructor(props) {
    super(props);
    this.state = {
        isModalVisible: props.modalVisible
    };
  };

  _setModalVisible(visible) {
    this.setState({modalVisible: visible});
  }

  render() {
    return (
        <View>
            <Modal
            animationType="slide"
            transparent={false}
            visible={this.state.modalVisible}
            onRequestClose={() => {alert("Modal has been closed.")}}
            >
                <View style={styles.container}>
                    <View style={styles.innerContainer}>
                        <Text>Item Detail</Text>
                        <TouchableHighlight
                            style={styles.buttonContainer}
                            onPress={() => { this._setModalVisible(false) }}>
                            <Text style={styles.buttonText}>Close</Text> 
                        </TouchableHighlight>
                    </View>
                </View>
            </Modal>
        </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    padding: 20,
    backgroundColor: 'transparent',
  },
  innerContainer: {
    borderRadius: 10,
    alignItems: 'center',
    backgroundColor: '#34495e',
 },
 buttonContainer: {
    paddingVertical: 15,
    marginTop: 20,
    backgroundColor: '#2c3e50',
    borderRadius: 15
 },
 buttonText: {
    textAlign: 'center',
    color: '#ecf0f1',
    fontWeight: '700'
 },
});

Current behaviour is MyModal ponent being displayed when I access to MyList ponent for the first time, I can close it and then the FlatList is there but when a list item is pressed, MyModal ponent is not displayed.

How can I manage to hide Modal and open it only when a list item is pressed?

Another doubt related to this is:

How to pass the pressed item object to MyModal ponent?

Thanks in advance!

What I am trying to achieve is to open a modal dialog when the user press on one of the items of a FlatList ponent. The code of this list looks like this:

class MyItem extends React.Component { 
  _onPress = () => { 
    this.props.onPressItem(this.props.item); 
  }; 
  render() { 
    return(
        <TouchableOpacity 
            {...this.props}
            onPress={this._onPress}
            >
            <Text style={styles.itemText}> {this.props.item.name}</Text>
        </TouchableOpacity>
    ) 
  } 
}

export default class MyList extends React.PureComponent {
    constructor(props) {
        super(props);
        this.state = {
            data: {}, // some data correctly loaded
            isModalVisible: false
        };
    };

    _onPressItem = (item) => { 
        this._showModal;
    };

    _showModal = () => this.setState({ isModalVisible: true })

    _keyExtractor = (item, index) => item.id;

    _renderItem = ({item}) => (
        <MyItem 
            style={styles.row}
            item={item}
            onPressItem={this._onPressItem}
        />
    );

    render() {
      return(
        <KeyboardAvoidingView behavior="padding" style={styles.container}>
          <View style={styles.titleContainer}>
              <Text style={styles.title}>Tittle</Text>
          </View>
          <ScrollView style={styles.container}>
              <FlatList 
                  data={this.state.data}
                  ItemSeparatorComponent = {this._flatListItemSeparator}
                  renderItem={this._renderItem}
                  keyExtractor={this._keyExtractor}
              />
          </ScrollView>
          <MyModal modalVisible={this.state.isModalVisible}/>
        </KeyboardAvoidingView>
      );
    }
}

Styles, FlatList data and some functions have been removed because they are not relevant for this issue.

As you can see, MyModal ponent is declared after ScrollView ponent. The code is based on the use of react-native Modal ponent:

export default class MyModal extends Component {
  constructor(props) {
    super(props);
    this.state = {
        isModalVisible: props.modalVisible
    };
  };

  _setModalVisible(visible) {
    this.setState({modalVisible: visible});
  }

  render() {
    return (
        <View>
            <Modal
            animationType="slide"
            transparent={false}
            visible={this.state.modalVisible}
            onRequestClose={() => {alert("Modal has been closed.")}}
            >
                <View style={styles.container}>
                    <View style={styles.innerContainer}>
                        <Text>Item Detail</Text>
                        <TouchableHighlight
                            style={styles.buttonContainer}
                            onPress={() => { this._setModalVisible(false) }}>
                            <Text style={styles.buttonText}>Close</Text> 
                        </TouchableHighlight>
                    </View>
                </View>
            </Modal>
        </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    padding: 20,
    backgroundColor: 'transparent',
  },
  innerContainer: {
    borderRadius: 10,
    alignItems: 'center',
    backgroundColor: '#34495e',
 },
 buttonContainer: {
    paddingVertical: 15,
    marginTop: 20,
    backgroundColor: '#2c3e50',
    borderRadius: 15
 },
 buttonText: {
    textAlign: 'center',
    color: '#ecf0f1',
    fontWeight: '700'
 },
});

Current behaviour is MyModal ponent being displayed when I access to MyList ponent for the first time, I can close it and then the FlatList is there but when a list item is pressed, MyModal ponent is not displayed.

How can I manage to hide Modal and open it only when a list item is pressed?

Another doubt related to this is:

How to pass the pressed item object to MyModal ponent?

Thanks in advance!

Share Improve this question edited Nov 3, 2017 at 10:27 juankysmith asked Oct 30, 2017 at 12:27 juankysmithjuankysmith 12.5k6 gold badges39 silver badges65 bronze badges 3
  • The first part may just be a typo - in MyList._onPressItem you're not actually calling _showModal (missing the brackets). – Rob Hogan Commented Oct 30, 2017 at 13:08
  • One other quick tip - FlatList is a scrolling view in itself - there's no need to wrap it in a ScrollView. – Rob Hogan Commented Oct 30, 2017 at 17:08
  • Thank you so much I removed the ScrollView and it works perfect, I am stuck trying to access the selectedItem in MyModal ponent, as I wrote in the ment to your answer, any sugestions? – juankysmith Commented Oct 30, 2017 at 17:18
Add a ment  | 

2 Answers 2

Reset to default 4

Passing the item through to the modal

To pass the selected item to your modal, you'll need to add it as a prop on your Modal ponent.

You can remember the selected item in MyList's state:

_onPressItem = (item) => { 
    this._showModal(item);
};

_showModal = (selectedItem) => this.setState({ isModalVisible: true, selectedItem })

And then pass it through to the modal when you render it from MyList:

// ...
      </ScrollView>
      <MyModal 
        modalVisible={this.state.isModalVisible} 
        selectedItem={this.state.selectedItem} />
    </KeyboardAvoidingView>
// ...

Controlling the modal's visibility

Currently you have a modal visibility boolean in both MyList's state (isModalVisible, which is passed through to MyModal as the modalVisible prop) and also in MyModal's state (modalVisible). There's no need for the last one - it'll just give you a headache trying to keep them in sync. Just use the props to "control" MyModal, keeping a single source of truth, and pass a callback to allow MyModal to tell MyList that the modal should be dismissed.

// ...
      </ScrollView>
      <MyModal 
        modalVisible={this.state.isModalVisible} 
        selectedItem={this.state.selectedItem}
        onDismiss={this._hideModal} />
    </KeyboardAvoidingView>
// ...

A new stateless MyModal:

export default class MyModal extends Component {    
  render() {
    return (
        <View>
            <Modal
            animationType="slide"
            transparent={false}
            visible={this.props.modalVisible}
            onRequestClose={() => { this.props.onDismiss() }}
            >
                <View style={styles.container}>
                    <View style={styles.innerContainer}>
                        <Text>Item Detail</Text>
                        <TouchableHighlight
                            style={styles.buttonContainer}
                            onPress={() => { this.props.onDismiss() }}>
                            <Text style={styles.buttonText}>Close</Text> 
                        </TouchableHighlight>
                    </View>
                </View>
            </Modal>
        </View>
    );
  }
}
class MyItem extends React.Component { 
  _onPress = () => { 
    this.props.onPressItem(this.props.item); 
  }; 
  render() { 
    return(
        <TouchableOpacity 
            {...this.props}
            onPress={this._onPress}
            >
            <Text style={styles.itemText}> {this.props.item.name}</Text>
        </TouchableOpacity>
    ) 
  } 
}

export default class MyList extends React.PureComponent {
    constructor(props) {
        super(props);
        this.state = {
            data: {}, // some data correctly loaded
            isModalVisible: false,
            selectedItem: null
        };
    };

    _onPressItem = (item) => { 
        this._showModal(item);
    };

    _hideMyModal = () => {
        this.setState({isModalVisible: false})
    }

    _showModal = (item) => this.setState({ isModalVisible: true, 
    selectedItem: item })

    _keyExtractor = (item, index) => item.id;

    _renderItem = ({item}) => (
        <MyItem 
            style={styles.row}
            item={item}
            onPressItem={() => this._onPressItem(item)}
        />
    );

    render() {
      return(
        <KeyboardAvoidingView behavior="padding" style={styles.container}>
          <View style={styles.titleContainer}>
              <Text style={styles.title}>Tittle</Text>
          </View>
          <ScrollView style={styles.container}>
              <FlatList 
                  data={this.state.data}
                  ItemSeparatorComponent = {this._flatListItemSeparator}
                  renderItem={this._renderItem}
                  keyExtractor={this._keyExtractor}
              />
          </ScrollView>
          { this.state.isModalVisible && <MyModal selectedItem={this.state.selectedItem} modalVisible={this.state.isModalVisible} hideModal={this_hideMyModal} /> }
        </KeyboardAvoidingView>
      );
    }
}

When you press the item in FlatList, just set it in the state and pass it as a prop to MyModal ponent(pretty self explanatory).

export default class MyModal extends Component {
  constructor(props) {
    super(props);
    this.state = {
        isModalVisible: props.modalVisible
    };
  };

  _setModalVisible(visible) {
    this.setState({modalVisible: visible});
  }

  render() {
    return (
        <View>
            <Modal
            animationType="slide"
            transparent={false}
            visible={this.state.isModalVisible}
            onRequestClose={() => {alert("Modal has been closed.")}}
            >
                <View style={styles.container}>
                    <View style={styles.innerContainer}>
                        <Text>Item Detail</Text>
                        <TouchableHighlight
                            style={styles.buttonContainer}
                            onPress={() => { this.props.hideModal() }}>
                            <Text style={styles.buttonText}>Close</Text> 
                        </TouchableHighlight>
                    </View>
                </View>
            </Modal>
        </View>
    );
  }
}

In your previous code in MyModal you were doing this --> onPress={() => { this._setModalVisible(false) }}> which will hide the modal in the MyModal class but MyModal is still rendered in the MyList class. So you might be unable to click the FlatList item again. I passed a prop hideModal which will remove the MyModal ponent from the MyList. In my revised code you don't need to pass modalVisible prop also, but its up to you for that.

发布评论

评论列表(0)

  1. 暂无评论
ok 不同模板 switch ($forum['model']) { /*case '0': include _include(APP_PATH . 'view/htm/read.htm'); break;*/ default: include _include(theme_load('read', $fid)); break; } } break; case '10': // 主题外链 / thread external link http_location(htmlspecialchars_decode(trim($thread['description']))); break; case '11': // 单页 / single page $attachlist = array(); $imagelist = array(); $thread['filelist'] = array(); $threadlist = NULL; $thread['files'] > 0 and list($attachlist, $imagelist, $thread['filelist']) = well_attach_find_by_tid($tid); $data = data_read_cache($tid); empty($data) and message(-1, lang('data_malformation')); $tidlist = $forum['threads'] ? page_find_by_fid($fid, $page, $pagesize) : NULL; if ($tidlist) { $tidarr = arrlist_values($tidlist, 'tid'); $threadlist = well_thread_find($tidarr, $pagesize); // 按之前tidlist排序 $threadlist = array2_sort_key($threadlist, $tidlist, 'tid'); } $allowpost = forum_access_user($fid, $gid, 'allowpost'); $allowupdate = forum_access_mod($fid, $gid, 'allowupdate'); $allowdelete = forum_access_mod($fid, $gid, 'allowdelete'); $access = array('allowpost' => $allowpost, 'allowupdate' => $allowupdate, 'allowdelete' => $allowdelete); $header['title'] = $thread['subject']; $header['mobile_link'] = $thread['url']; $header['keywords'] = $thread['keyword'] ? $thread['keyword'] : $thread['subject']; $header['description'] = $thread['description'] ? $thread['description'] : $thread['brief']; $_SESSION['fid'] = $fid; if ($ajax) { empty($conf['api_on']) and message(0, lang('closed')); $apilist['header'] = $header; $apilist['extra'] = $extra; $apilist['access'] = $access; $apilist['thread'] = well_thread_safe_info($thread); $apilist['thread_data'] = $data; $apilist['forum'] = $forum; $apilist['imagelist'] = $imagelist; $apilist['filelist'] = $thread['filelist']; $apilist['threadlist'] = $threadlist; message(0, $apilist); } else { include _include(theme_load('single_page', $fid)); } break; default: message(-1, lang('data_malformation')); break; } ?>