I followed https://facebook.github.io/react-native/releases/next/docs/flatlist.html to make a FlatList and also passed this.state to extraData but still, once I delete an item, the item is still shown. I have also logged this.state to make sure the item is deleted and it indeed did. My code is below:
class DescriptionItem extends React.PureComponent {
render() {
return (
<TouchableOpacity
style={this.props.containerStyle}
onPress={(event) => {this.props.onPress(this.props.value)}}>
<Text style={this.props.style}>{this.props.value}</Text>
</TouchableOpacity>
)
}
}
export default class CardWorkDescriptionsList extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
error: null,
refreshing: false,
};
}
_notifyItemSelected(text) {
if(this.props.onItemSelected) {
this.props.onItemSelected(text)
}
}
_onItemSelected = (selectedItem) => {
var array = this.state.data;
var index = array.indexOf(selectedItem)
array.splice(index, 1);
console.log(array)
this.setState({data: array });
console.log("yeah",this.state.data)
this._notifyItemSelected(selectedItem);
}
makeRemoteRequest = () => {
//TODO: fetch data
this.setState({data: descriptionsFake})
}
componentDidMount() {
this.makeRemoteRequest();
}
render() {
return (
<View style={styles.cardLight}>
<FlatList
data={this.state.data}
extraData={this.state}
renderItem={(item) => (
<DescriptionItem
containerStyle={styles.itemContainer}
style={styles.content}
value={item.item}
onPress={(selectedItem)=>this._onItemSelected(selectedItem)}/>
)}
keyExtractor={item => item.substring(0,20)}
/>
</View>
);
}
}
Try extending React.Component instead of PureComponent
Related
I'm new to React Native Would like to get any good solution for following task:
Create one application which will call one API
(https://api.napster.com/v2.0/playlists?apikey=ZTk2YjY4MjMtMDAzYy00MTg4LWE2MjYtZDIzNjJmMmM0YTdm).
Using thread and store it in some file.
Current Code
class App extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: null,
}
}
componentDidMount() {
return fetch('https://api.napster.com/v2.0/playlists?apikey=ZTk2YjY4MjMtMDAzYy00MTg4LWE2MjYtZDIzNjJmMmM0YTdm')
.then(response => response.json())
.then((responseJSON) => {
this.setState({
isLoading: false,
dataSource: responseJSON.playlists,
})
})
.catch((error) => {
console.log(error);
})
}
render() {
if (this.state.isLoading) {
return (
<View style={styles.container}>
<ActivityIndicator />
</View>
)
} else {
let dbSource = this.state.dataSource.map((val, key) => {
return (
<View key={key} style={styles.item}>
<Text>{val.name}</Text>
</View>
)
})
return (
<View style={styles.container}>
{dbSource}
</View>
);
}
}
};
I have a Main class which I show an array to user, then in detail page user can edit each element which I'm passing using react navigation parameter. I want to edit my array in the detail class and save it using async storage.
//Main.jsimport React from 'react';
import {
StyleSheet ,
Text,
View,
TextInput,
ScrollView,
TouchableOpacity,
KeyboardAvoidingView,
AsyncStorage
} from 'react-native'
import Note from './Note'
import detail from './Details'
import { createStackNavigator, createAppContainer } from "react-navigation";
export default class Main extends React.Component {
static navigationOptions = {
title: 'To do list',
headerStyle: {
backgroundColor: '#f4511e',
},
};
constructor(props){
super(props);
this.state = {
noteArray: [],
noteText: '',
dueDate: ''
};
}
async saveUserTasks(value) {
try {
await AsyncStorage.setItem('#MySuperStore:userTask',JSON.stringify(value));
} catch (error) {
console.log("Error saving data" + error);
}
}
getUserTasks = async() =>{
try {
const value = await AsyncStorage.getItem('#MySuperStore:userTask');
if (value !== null){
this.setState({ noteArray: JSON.parse(value)});
}
} catch (error) {
console.log("Error retrieving data" + error);
}
}
render() {
this.getUserTasks()
let notes = this.state.noteArray.map((val,key) => {
return <Note key={key} keyval={key} val={val}
deleteMethod={ () => this.deleteNote(key)}
goToDetailPage= {() => this.goToNoteDetail(key)}
/>
});
const { navigation } = this.props;
return(
<KeyboardAvoidingView behavior='padding' style={styles.keyboard}>
<View style={styles.container}>
<ScrollView style={styles.scrollContainer}>
{notes}
</ScrollView>
<View style={styles.footer}>
<TextInput
onChangeText={(noteText) => this.setState({noteText})}
style={styles.textInput}
placeholder='What is your next Task?'
placeholderTextColor='white'
underlineColorAndroid = 'transparent'
>
</TextInput>
</View>
<TouchableOpacity onPress={this.addNote.bind(this)} style={styles.addButton}>
<Text style={styles.addButtonText}> + </Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
);
}
addNote(){
if (this.state.noteText){
var d = new Date();
this.state.noteArray.push({
'creationDate': d.getFullYear() + "/" + (d.getMonth()+1) + "/" + d.getDay(), 'taskName': this.state.noteText,'dueDate':'YYYY/MM/DD'
});
this.setState({noteArray:this.state.noteArray})
this.setState({noteText: ''});
this.saveUserTasks(this.state.noteArray)
}
}
deleteNote(key){
this.state.noteArray.splice(key,1);
this.setState({noteArray: this.state.noteArray})
this.saveUserTasks(this.state.noteArray)
}
goToNoteDetail=(key)=>{
this.props.navigation.navigate('DetailsScreen', {
selectedTask: this.state.noteArray[key],
});
}
}
in detail view I have this method which is similar to add note in main class:
export default class Details extends React.Component {
render() {
const { navigation } = this.props;
const selectedTask = navigation.getParam('selectedTask', 'task');
return(
<View key={this.props.keyval} style={styles.container}>
<TouchableOpacity onPress={this.saveEdit.bind(this)} style={styles.saveButton}>
<Text style={styles.saveButtonText}> save </Text>
</TouchableOpacity>
</View>
);
}
saveEdit(){
let selectedItem = { 'creationDate': selectedTask['creationDate'],
'taskName': selectedTask['taskName'],
'dueDate': this.state.dueData}
this.props.navigation.state.params.saveEdit(selectedItem)
}
}
How can I change my props in any component?
First of all you shouldn't call this.getUserTasks() in the render method because the function has this.setState which is bad and could end in a endless loop I guess or at least effect in worse performance. You could instead call it in componentDidMount:
componentDidMount = () => {
this.getUserTasks();
}
Or alternatively call already in constructor but I prefer the first option:
constructor(props){
super(props);
this.state = {
noteArray: [],
noteText: '',
dueDate: ''
};
this.getUserTasks()
}
this.props.noteArray.push({.. is probably undefined because you aren't passing it down any where. (Didn't see any reference in your snippet). I guess I would implement the saveEdit function in the Main.js component and simply pass it down to the navigation route and call the function in Details component by accessing the navigation state props:
Update
goToNoteDetail=(key)=>{
this.props.navigation.navigate('DetailsScreen', {
// selectedTask: this.state.noteArray[key],
selectedItem: key,
saveEdit: this.saveEdit
});
}
saveEdit(selectedItem){
const selectedTask = this.state.noteArray[selectedItem]
this.state.noteArray.push({
'creationDate': selectedTask['creationDate'],
'taskName': selectedTask['taskName'],
'dueDate': this.state.dueData
});
this.setState({noteArray:this.state.noteArray})
this.setState({dueData: 'YYYY/MM/DD'});
this.saveUserTasks(this.state.noteArray)
}
And then call saveEdit in Details Component:
saveSelectedItem = () => {
const { navigation } = this.props.navigation;
const {selectedItem, saveEdit} = navigation.state && navigation.state.params;
saveEdit(selectedItem)
}
I filled an array with querySnapshot but flatlist doesn't render anything
Tried to change renderItem code
constructor(props) {
super(props);
var rootref = firebase.firestore().collection("deneme");
var wholeData = []
rootref.get().then(function(querySnapshot){
querySnapshot.forEach(function(doc) {
wholeData.push(doc.data())
});
});
};
render() {
return (
<View>
<FlatList
data={this.wholeData}
renderItem={({item}) => <Text>{item.isim}, {item.soyisim}</Text>}
/>
</View>
);
};
You will have to use setState to notify the component that the data has changed. Change your code to do the following:
constructor(props) {
super(props);
this.state = {
data: []
};
var rootref = firebase.firestore().collection("deneme");
rootref.get().then(querySnapshot =>
const wholeData = querySnapshot.map(doc => doc.data()));
// notify your component that the data has changed
this.setState({
data: wholeData
})
};
render() {
return (
<View>
<FlatList
data={this.state.data} // get your data from the state
renderItem={({item}) => <Text>{item.isim}, {item.soyisim}</Text>}
/>
</View>
);
This way as soon as you have received the wholeData, the FlatList will update.
I created a SectionList and tried to implement a search filter for my SectionList. But my output got an error. I took a screenshot of it below. I don't know what's wrong.
This is my component.
export default class Cluster1 extends Component{
constructor(props){
super(props)
this.state = {
dataToShow: '',
search: false
}
}
searchUpdated = (term) => {
let matchedItemsArray = []
if(term === ''){
this.setState({search: false, dataToShow: ClusterData})
}else{
this.state.dataToShow.map((item) => {
if(item.title.includes(term)){
matchedItemsArray.push(item)
}
})
this.setState({search: true, dataToShow: matchedItemsArray})
}
}
searchUpdated = (input) => {
let userInput =[]
if(input === ''){
this.setState({search: false})
userInput = ''
}else{
this.setState({search: true})
}
}
render(){
return(
<View style={styles.container}>
<TextInput
onChangeText={(term) => { this.searchUpdated(text) }}
style={styles.searchInput}
placeholder="Type a mood to search"
/>
<SectionList
renderItem = {({item, index}) =>
<SectionListItem item = {item} index = {index}/>}
renderSectionHeader = {({section}) =>
<SectionHeader
sections={this.searchUpdated()}
keyExtractor = {(item) => item.name}/>}>
</SectionList> </View>
);
}}
class SectionHeader extends Component {
render() {
return (
<View style={styles.header}>
<Text style={styles.headertext}>
{this.props.section.title}
</Text>
<TouchableOpacity onPress={ () => Actions.SongList({ section: this.props.section}) }>
<Text style ={styles.Play}> Play
</Text>
</TouchableOpacity>
</View>
); }
}
class SectionListItem extends Component{
render(){
return(
<View>
<Text style={styles.moodname}>{this.props.item.name}</Text>
</View>
);
}}
This is my data
const ClusterData = [
{ title: 'Cluster1',
data:
[
{name: 'passionate'},{name: 'rousing'},{name: 'confident'},
{name: 'boisterous'},{name: 'rowdy'}],
},
{
title: 'Cluster2',
data:
[
{name: 'rollicking'},{name: 'cheerful'{name: 'fun'},{name: 'sweet'},
{name: 'amiable'},{name: 'natured'}],
Here is a simple search filter:
I added a search state to help determine whether the user is currently searching or not.
constructor(props){
super(props)
this.state = {
dataToShow: '',
search: false
}
}
Then, we create the search function.
searchUpdated = (term) => {
let matchedItemsArray = []
if(term === ''){
this.setState({search: false, dataToShow: ClusterData})
}else{
this.state.dataToShow.map((item) => {
if(item.title.includes(term)){
matchedItemsArray.push(item)
}
})
this.setState({search: true, dataToShow: matchedItemsArray})
}
}
When the input is '', the search state is false. Otherwise, the function will map through the dataToShow array to find if any section titles include the user's input.
Alternatively, I like to use a lodash filter instead for it's simplicity.
First, we declare a constant called userInput:
let userInput
Then, we create a function to determine whether the userInput is empty or not to set the search state. (Remember to keep this.state.search that we created in the first place)
searchUpdated = (input) => {
if(input === ''){
this.setState({search: false})
userInput = ''
}else{
this.setState({search: true})
}
}
Finally, in our SectionList we use the lodash filter to help filter for the right section header names:
<SectionList
renderItem = {({item, index}) =>
<SectionListItem item = {item} index = {index}/>}
renderSectionHeader = {({section}) =>
<SectionHeader
section = {section}
sections = {
this.state.search ?
_.filter(this.state.dataToShow, function(item){
return item.title.includes(userInput)})
: this.state.dataToShow}
keyExtractor = {(item) => item.name}/>}>
</SectionList>
The entire component
import React from 'react'
import { View, Text, SectionList, TouchableOpacity, TextInput } from 'react-native'
const ClusterData = [
{title: 'Cluster1', data: [{name: 'passionate'},{name: 'rousing'},{name: 'confident'},{name: 'boisterous'},{name: 'rowdy'}]},
{title: 'Cluster2', data: [{name: 'rollicking'},{name: 'cheerful'},{name: 'fun'},{name: 'sweet'},{name: 'amiable'},{name: 'natured'}]}
]
let userInput = ''
export default class TempScreen extends React.Component {
constructor(props){
super(props)
this.state = {
search: false,
dataToShow: []
}
}
componentWillMount(){
this.setState({dataToShow: ClusterData})
}
searchUpdated = (term) => {
let matchedItemsArray = []
if(term === ''){
this.setState({search: false, dataToShow: ClusterData})
}else{
this.setState({search:true, dataToShow: ClusterData}, function(){
this.state.dataToShow.map((item) => {
if(item.title.includes(term)){
matchedItemsArray.push(item)
}
})
this.setState({dataToShow:matchedItemsArray})
})
}
}
render () {
return (
<View>
<TextInput
onChangeText={(term) => {this.searchUpdated(term)}}
style={styles.searchInput}
placeholder="Type a mood to search"/>
<SectionList
renderItem={({item}) => <SectionListItem itemName = {item.name}/>}
renderSectionHeader={({section}) => <SectionHeader sectionTitle = {section.title}/>}
sections={this.state.dataToShow}
/>
</View>
)
}
}
class SectionHeader extends React.Component{
render(){
return(
<View>
<Text>{this.props.sectionTitle}</Text>
<TouchableOpacity>
<Text>Play</Text>
</TouchableOpacity>
</View>
)
}
}
class SectionListItem extends React.Component{
render(){
return(
<View>
<Text>{this.props.itemName}</Text>
</View>
)
}
}
How can I access class functions from inside stack navigator header? Is this possible?
What I'm trying to achieve is to call a function when I press the stack navigator header title.
class Dashboard extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
headerTitle: (
<View style={header.addressView}>
<Text
style={header.titleAddress}
onPress={() => {
this._show().bind(this);
}}>
/>
</View>
),
};
};
_show(){
this.setState({ visibleModal: 1 })
}
constructor(props) {
super(props);
this.state = {
visibleModal: null,
};
}
render() {
...........
}
}
export default Dashboard;
class Dashboard extends React.Component {
static navigationOptions = ({ navigation }) => {
const showParams = navigation.getParam("show",()=>{});
//Access the params like this.
return {
headerTitle: (
<View style={header.addressView}>
<Text
style={header.titleAddress}
onPress={showParams}>
/>
</View>
),
};
};
_show(){
this.setState({ visibleModal: 1 })
}
//Too add this.
componentDidMount(){
this.props.navigation.setParams({show:()=>this._show()});
}
constructor(props) {
super(props);
this.state = {
visibleModal: null,
};
}
render() {
...........
}
}
export default Dashboard;
This is how you access the variables or states inside a class and make it available for the static navigationOptions function.
Reference