Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Key issues when using FlatList #38

Closed
chrisrobbins opened this issue Aug 28, 2017 · 12 comments
Closed

Key issues when using FlatList #38

chrisrobbins opened this issue Aug 28, 2017 · 12 comments

Comments

@chrisrobbins
Copy link

When trying to render swipeable in FlatList component, i get the following error:

ExceptionsManager.js:71 Warning: Each child in an array or iterator should have a unique "key" prop.

Check the render method of Swipeable. It was passed a child from DiscogsSearch. See https://fb.me/react-warning-keys for more information.

Is Swipeable compatible with Flatlist?

Also the swipe actions fire multiple times on one swipe, but i'm not sure if that is a related issue.

@martimarkov
Copy link

Check #3

As for the key prop, you either have to pass the key prop to your renderItem function:

          <Swipeable
            key={item.id}
           >
          {item.title}
          </Swipeable>
        )}

or implement keyExtractor for the FlatList:

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

           <FlatList 
            keyExtractor={this._keyExtractor}
           ...

           </FlatList>

@chrisrobbins
Copy link
Author

Thank you for your reply. I have tried all of these things and I still get the same errors. I tried burning it down and rebuilding the component and my problem now is that I cant get the swipeable components to activate when setting state. If I remove the setState event and put a log there it works fine.

`import React, { Component } from 'react';
import axios from 'axios';
import { connect } from 'react-redux';
import {
saveCollectionItem,
fetchCollection,
} from '../actions/collection-action';
import {
saveWantlistItem,
fetchWantlist,
} from '../actions/wantlist-action';

import {
Button,
BarCode,
ClearText
} from '../components/common';
import fire from '../components/fire';
import SearchResultItem from '../components/SearchResultItem';
import Swipeable from 'react-native-swipeable';

import _ from 'lodash';

import {
View,
Text,
TextInput,
Image,
ActivityIndicator,
FlatList
} from 'react-native';

class DiscogsSearch extends Component {
constructor(props) {
super(props);
this.searchDiscogs = this.searchDiscogs.bind(this)

this.state = {
  text: '',
  loading: false,
  albums: [],
  page: 1,
  discogsRecord: '',
  seed: 1,
  error: null,
  refreshing: false,
  leftActionActivated: false,
  toggle: false
};
this.searchDiscogs = _.debounce(this.searchDiscogs, 200)

}
static navigationOptions = {

tabBarIcon: ({ tintColor }) => (tintColor == '#e91e63' ?
<Image
  source={require('../img/search_select.png')}
/>
:
<Image
  source={require('../img/search.png')}
/>

),
};

componentWillMount() {
console.log(this.props, "HELLOOOO");
}

searchDiscogs = () => {
const apiKey = "jbUTpFhLTiyyHgLRoBgq";
const apiSecret = "LSQDaLpplgcCGlkzujkHyUkxImNlWVoI";
const { page } = this.state;
const apiSearch = this.state.newText;
const url = https://api.discogs.com/database/search?artist=${apiSearch} &key=${apiKey}&secret=${apiSecret}
this.setState({ loading: true });

axios.get(url)
  .then(res => {
    this.setState({
      albums:page === 1 ? res.data.results : [...this.state.albums, ...res.data.results],
      error: res.error || null,
      loading: false,
      refreshing: false
    });
    console.log(apiSearch, res.data.results);
  })
  .catch(error => {
    this.setState({ error, loading: false });
  });

};

clearTextInput = () => {
this._textInput.setNativeProps({ text: '' });
this.setState({ text: '', albums: [] });
}
renderInputButton = () => {
return ;
}
// handleScroll = () => {
// const {currentlyOpenSwipeable} = this.state;
//
// if (currentlyOpenSwipeable) {
// currentlyOpenSwipeable.recenter();
// }
// };

handleRefresh = () => {
this.setState(
{
page: 1,
seed: this.state.seed + 1,
refreshing: true
},
() => {
this.searchDiscogs;
}
);
};

handleLoadMore = () => {
this.setState(
{
page: this.state.page + 1
},
() => {
this.searchDiscogs;
}
);
};

renderFooter = () => {
if (!this.state.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: "#CED0CE"
}}
>


);
};
_keyExtractor = (item, index) => index;
FlatListItemSeparator = () => {
return (
<View
style={{
height: 1,
width: "100%",
backgroundColor: "#607D8B",
}}
/>
);
}

render() {
const { leftActionActivated, rightActionActivated, toggle } = this.state;
const wantlistIcon = require('../img/wantlistButton.png');
const collectionIcon = require('../img/collectionButton.png');
const check = require('../img/check-o.png');

const leftContent = [
  <View style={[styles.leftSwipeItem, {backgroundColor: leftActionActivated ? 'lightgoldenrodyellow' : 'steelblue'}]}>
        {leftActionActivated ?
          <Text>release!</Text> :
          <Text>keep pulling!</Text>}
      </View>
];

const rightContent = [
  <View style={[styles.rightSwipeItem, {backgroundColor: rightActionActivated ? '#F4702E' : '#000'}]}>
  {rightActionActivated ?
    <Image style={styles.rightIconStyles} source={check} /> :
    <Image style={styles.rightIconStyles} source={wantlistIcon} />}
  </View>
];
return (
  <View style={styles.container}>

    <View style={styles.inputStyleContainer}>


    <TextInput

      ref={text => this._textInput = text}

      style={styles.inputStyle}

      autoFocus={true}

      type="search"

      value={this.state.newText}

      onChangeText={this.searchDiscogs}

      onChange={(event) => this.setState({ newText: event.nativeEvent.text })}

      placeholder="Artist or Album"

      placeholderTextColor="#D9D9D9"

      selectionColor={'#F42E4A'}

    />

  </View>

  <View style={styles.inputContainer}>

    {this.renderInputButton()}

  </View>
    <FlatList
      data={this.state.albums}
      renderItem={({item}) => (
        <Swipeable
          leftContent= {(
  <View style={[styles.leftSwipeItem, {backgroundColor: leftActionActivated ? 'lightgoldenrodyellow' : 'steelblue'}]}>
    {leftActionActivated ?
      <Text>release!</Text> :
      <Text>keep pulling!</Text>}
  </View>
)}
        onLeftActionActivate={() => this.setState({leftActionActivated:true})}
        onLeftActionDeactivate={() => this.setState({leftActionActivated: false})}

          onSwipeStart={() => this.setState({isSwiping: true})}
          onSwipeRelease={() => this.setState({isSwiping: false})}


          >

        <SearchResultItem
         item={item}
         collectionRecords={this.props.collection.collection.albums}
         {...this.props}
       />
       </Swipeable>
   )}
      keyExtractor={this._keyExtractor}
      ListFooterComponent={this.renderFooter}
      refreshing={this.state.refreshing}
      onEndReached={this.handleLoadMore}
      onEndReachedThreshold={40}
      style={styles.renderAlbums}
      scrollEnabled={!this.state.isSwiping}
      // backgroundColor={'#1A1A1A'}
      // itemBackgroundColor={'#1A1A1A'}

    />

</View>
);

}
}

const styles = {
renderAlbums: {
flex: 1,
marginTop: -3
},
inputContainer: {
justifyContent: 'flex-end',
alignItems: 'flex-end',
height: 5,
marginRight: 10,
marginBottom: 0,
},
container: {
flex: 1
},

inputStyleContainer: {
flexDirection: 'column',
alignItems: 'flex-start',
marginTop: 25,
height: 40,
borderBottomWidth: 1,
borderBottomColor: "#ffffff",
marginBottom: 0
},
inputStyle: {
color: '#fff',
fontSize: 18,
lineHeight: 23,
backgroundColor: '#000',
justifyContent: 'flex-start',
flex: 1,
paddingLeft: 7,
paddingRight: 7,
paddingBottom: 0,
marginBottom: 0
},
leftSwipeItem: {
flex: 1,
alignItems: 'flex-end',
justifyContent: 'center',
paddingRight: 20
}
};

const mapStateToProps = (state) => {
return {
...state
}
}
// for click events so that dispatches can happen
const mapDispatchToProps = (dispatch) => {
return {
saveCollectionItem: (item) => {
dispatch(saveCollectionItem(item))
},
saveWantlistItem: (item) => {
dispatch(saveWantlistItem(item))
},
fetchCollection: () => {
dispatch(fetchCollection())
},
fetchWantlist: () => {
dispatch(fetchWantlist())
},
}
}

export default connect(mapStateToProps, mapDispatchToProps)(DiscogsSearch);
`
Do you see anything obvious that could be causing this?

@martimarkov
Copy link

What do you mean by: I cant get the swipeable components?

@chrisrobbins
Copy link
Author

onLeftActionActivate is not activating at the threshold when trying to update state. it works fine with a console log but not when changing state.

@martimarkov
Copy link

Hmmm. Does console log work when state is there as well?

console.log(...)
this.setState(...)

@chrisrobbins
Copy link
Author

chrisrobbins commented Sep 11, 2017

ok with the console log and the setState it now has the opposite problem. It's always firing. Before It said Keep Pulling and would never change to Release, now it's only saying release and the log says it is firing over 50 times with a single swipe. I have followed the steps for issue #3 and no luck. The only difference I can see between the example and my code is I am using react-native 0.44.0 react 16.0.0.alpha-6 and the FlatList component.

@chrisrobbins
Copy link
Author

Logging the state onLeftActionActivated shows that OnLeftActionActivated is logging at the correct threshold but not setting the state. Any clues?

` <FlatList
data={this.state.albums}
renderItem={({item}) => (
<Swipeable
leftContent= {leftContent}
leftActionActivationDistance={200}
onLeftActionActivate={() => { this.setState({leftActionActivated:true})
console.log("action triggered", this.state.leftActionActivated)
}
}
onLeftActionDeactivate={() =>
this.setState({leftActionActivated: false})
}

          onSwipeStart={() => this.setState({isSwiping: true})}
          onSwipeRelease={() => this.setState({isSwiping: false})}


          >

        <SearchResultItem
         item={item}
         collectionRecords={this.props.collection.collection.albums}
         {...this.props}
       />
       </Swipeable>
   )}
      keyExtractor={this._keyExtractor}
      ListFooterComponent={this.renderFooter}
      refreshing={this.state.refreshing}
      onEndReached={this.handleLoadMore}
      onEndReachedThreshold={40}
      style={styles.renderAlbums}
      scrollEnabled={!this.state.isSwiping}
      // backgroundColor={'#1A1A1A'}
      // itemBackgroundColor={'#1A1A1A'}

    />

`

screen shot 2017-09-11 at 10 31 41 pm

@martimarkov
Copy link

martimarkov commented Sep 12, 2017 via email

@chrisrobbins
Copy link
Author

I logged onLeftActionActivated and onLeftActionDeactivated. when they are fired they both register false even though onLeftActionActivated has setState=({onLeftActionActivated=true}).

screen shot 2017-09-12 at 9 33 20 am

@martimarkov
Copy link

Can you paste the <Swipable /> code for the above screenshot?

@chrisrobbins
Copy link
Author

`const leftContent = [
<View style={[styles.leftSwipeItem, {backgroundColor: leftActionActivated ? 'green' : '#000'}]}>
{leftActionActivated ?
release! :
keep pulling!}

];

const rightContent = [
<View style={[styles.rightSwipeItem, {backgroundColor: rightActionActivated ? '#F4702E' : '#000'}]}>
{rightActionActivated ?
:
}

];

return (
<Swipeable
leftContent={leftContent}
leftActionActivationDistance={200}
onLeftActionActivate={() => {
this.setState({onLeftActionActivate: true})
console.log("action activated", "onLeftActionActivated: ", leftActionActivated)
}
}
onLeftActionDeactivate={() => {
this.setState({onLeftActionActivate: false})
console.log("action deactivated", "leftActionActivated: ", leftActionActivated)
}
}

  onSwipeStart={this.props.onSwipeStart}
  onSwipeRelease={this.props.onSwipeRelease}


  >
  <CardSection>
    <View style={imageView}>
      {!item.thumb ? <Image
        style={imageStyle}
        source={require('../img/n-a.png')}
      /> : <Image
        style={imageStyle}
        source={{ uri: item.thumb }}
      />}
    </View>

  <View style={textView}>
      <Text style={titleTextStyle}>{title}</Text>
      <Text style={artistTextStyle}>{artist}</Text>
    </View>
  </CardSection>
</Swipeable>
);

}
}
`

@powdahound
Copy link

Had this issue as well, though it was only showing up on iOS. Resolved it by adding a key prop to the highest level component to my Swipeable's rightContent (a <View> in my case).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants