I have 3 records in my table, I can see the app fetches record to my remote because I console.log the response. My problem is that it will not display the item.
I know I defined correctly the column in FlatList because If I will set the per_page=1 which means pull 1 record every request. It will display but 2 records only will display the last record will not, if I set to per_page=30 nothing displays. is there a problem in my setState() during the response ?.I heard that setSate is not mutable..how can I apply the updater function of setsate in my code.?...I am still fresh on react native I hope someone will help me here.
I tried to do this but no luck!..also is this will matter that I use react-redux in my other page then in this screen I did not use only handling of state. ?...please help me react-native experts.
this.setState({
page: this.getParameterByName('page', res.next_page_url),
data: this.state.page === 1 ? res.data : [...this.state.data, ...res.data],
error: res.error || null,
loading: false,
refreshing: false,
last_page: res.last_page
},()=>{
return this.state;
});
Here is my complete code
import React, { Component } from 'react';
import {ScrollView, Text, View, Button, FlatList, ActivityIndicator} from 'react-native';
import { List, ListItem, Icon } from "react-native-elements";
import {connect} from "react-redux";
import numeral from "numeral";
import Moment from 'react-moment';
import moment from 'moment';
class Screen1 extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
page: 1,
per_page: 30,
order_by:'id',
sort_by:'asc',
error: null,
refreshing: false,
param:'',
last_page:''
};
}
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const {page, per_page,order_by,sort_by } = this.state;
const url = `http://myapp.com/api/mobile/credit?page=${page}&api_token=${this.props.token}&per_page=${per_page}&order_by=${order_by}&sort_by=${sort_by}`;
console.log("the url",url);
this.setState({ loading: true });
setTimeout(()=>{
fetch(url)
.then(res => res.json())
.then(res => {
console.log("the page is =",this.getParameterByName('page',res.next_page_url));
this.setState({
page:this.getParameterByName('page',res.next_page_url),
data: this.state.page === 1 ? res.data : [...this.state.data,...res.data],
error: res.error || null,
loading: false,
refreshing: false,
last_page: res.last_page
});
})
.catch(error => {
this.setState({ error, loading: false });
});
},1500);
};
handleRefresh = () => {
if( this.state.page) {
if (this.state.page <= this.state.last_page) {
this.setState(
{
refreshing: true,
page: this.state.page
},
() => {
this.makeRemoteRequest();
}
);
}
}
};
getParameterByName = (name,url) =>{
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
let regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return parseInt(decodeURIComponent(results[2].replace(/\+/g, " ")), 10);
};
handleLoadMore = () => {
if( this.state.page){
if( this.state.page <= this.state.last_page ){
this.setState(
{
page: this.state.page
},
() => {
this.makeRemoteRequest();
}
);
}else{
console.log("cannot handle more",this.state.page)
}
}else{
console.log("page is null");
}
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "86%",
backgroundColor: "#CED0CE",
marginLeft: "14%"
}}
/>
);
};
renderHeader = () => {
return (
<View >
<Text h1
style={{
color: 'blue',
fontWeight: 'bold',
textAlign: 'center',
fontSize: 30,
backgroundColor: "#CED0CE",
}}
>{ numeral(this.props.thetotalcredit).format("#,##0.00") }</Text>
</View>
);
};
renderFooter = () => {
if (!this.state.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: "#CED0CE"
}}
>
<ActivityIndicator animating size="large" />
</View>
);
};
render() {
return (
<FlatList
data={this.state.data}
keyExtractor = {(item, index) => index.toString()}
renderItem={({ item }) => (
<ListItem
title= { numeral(item.amountcredit).format("#,##0.00") }
subtitle= { moment(item.creditdate).format("MMM DD, YYYY") }
containerStyle={{ borderBottomWidth: 0 }}
/>
)}
extraData={this.state.data}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderFooter}
refreshing={this.state.refreshing}
onRefresh={this.handleRefresh}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={0.5}
stickyHeaderIndices={[0]}
/>
);
}
}
const mapStateToProps = (state) => {
return {
username: state.auth.username,
token:state.auth.token,
thetotalcredit:state.auth.total_credit
};
};
export default connect(mapStateToProps)(Screen1);
Related
I'm new to react native and I'm trying to upload multiple images using expo-image-picker-multiple but I'm having an error with the code so I'm trying to convert the class component to functional components so I can easily deal with it since I'm more familiar with functional components.
export default class ImageBrowse extends Component {
_getHeaderLoader = () => (
<ActivityIndicator size='small' color={'#0580FF'} />
);
imagesCallback = (callback) => {
const { navigation } = this.props;
this.props.navigation.setOptions({
headerRight: () => this._getHeaderLoader()
});
callback.then(async (photos) => {
const cPhotos = [];
for (let photo of photos) {
const pPhoto = await this._processImageAsync(photo.uri);
cPhotos.push({
uri: pPhoto.uri,
name: photo.filename,
type: 'image/jpg'
})
}
navigation.navigate('Main', { photos: cPhotos });
})
.catch((e) => console.log(e));
};
async _processImageAsync(uri) {
const file = await ImageManipulator.manipulateAsync(
uri,
[{ resize: { width: 1000 } }],
{ compress: 0.8, format: ImageManipulator.SaveFormat.JPEG }
);
return file;
};
_renderDoneButton = (count, onSubmit) => {
if (!count) return null;
return <TouchableOpacity title={'Done'} onPress={onSubmit}>
<Text onPress={onSubmit}>Done</Text>
</TouchableOpacity>
}
updateHandler = (count, onSubmit) => {
this.props.navigation.setOptions({
title: `Selected ${count} files`,
headerRight: () => this._renderDoneButton(count, onSubmit)
});
};
renderSelectedComponent = (number) => (
<View style={styles.countBadge}>
<Text style={styles.countBadgeText}>{number}</Text>
</View>
);
render() {
const emptyStayComponent = <Text style={styles.emptyStay}>Empty =(</Text>;
return (
<View style={[styles.flex, styles.container]}>
<ImageBrowser
max={4}
onChange={this.updateHandler}
callback={this.imagesCallback}
renderSelectedComponent={this.renderSelectedComponent}
emptyStayComponent={emptyStayComponent}
/>
</View>
);
}
}
Also this is related to the above code:
export default class MainScreen extends Component {
constructor(props) {
super(props)
this.state = {
photos: []
}
}
componentDidUpdate() {
const { params } = this.props.route;
if (params) {
const { photos } = params;
if (photos) this.setState({ photos });
delete params.photos;
}
}
renderImage(item, i) {
return (
<Image
style={{ height: 100, width: 100 }}
source={{ uri: item.uri }}
key={i}
/>
)
}
render() {
const { navigate } = this.props.navigation;
return (
<View style={{ flex: 1 }}>
<Button
title="Open image browser"
onPress={() => { navigate('ImageBrowser'); }}
/>
<ScrollView>
{this.state.photos.map((item, i) => this.renderImage(item, i))}
</ScrollView>
</View>
);
}
}
The error is when I browse the gallery to select images nothing is showing:
And this warning showing to me:
Also this is what i came up with when I tried to convert class to functional components:
const PropertiesInfo = ({ navigation }) => {
const [attachments, setAttachments] = useState([]);
const componentDidUpdate=()=> {
const { params } = attachments.route;
if (params) {
const { attachments } = params
if (attachments) setAttachments({ attachments })
delete params.attachments
}
};
const renderImage=(item, i) =>{
return (
<Image
style={{ height: 100, width: 100 }}
source={{ uri: item.uri }}
key={i}
/>
)
};
return (
<View style={{ flex: 1 }}>
<Button
title="Open image browser"
onPress={() => { navigate('ImageBrowser'); }}
/>
<ScrollView>
{attachments.map((item, i) => renderImage(item, i))}
</ScrollView>
</View>
);
};
export default PropertiesInfo;
And this also:
const ImageBrowse=({ navigation })=> {
_getHeaderLoader = () => (
<ActivityIndicator size='small' color={'#0580FF'} />
);
imagesCallback = (callback) => {
navigation.setOptions({
headerRight: () => _getHeaderLoader()
});
callback.then(async (attachments) => {
const cPhotos = [];
for (let photo of attachments) {
const pPhoto = await _processImageAsync(photo.uri);
cPhotos.push({
uri: pPhoto.uri,
name: photo.filename,
type: 'image/jpg'
})
}
navigation.navigate('Main', { attachments: cPhotos });
})
.catch((e) => console.log(e));
};
const _processImageAsync = async (uri) => {
const file = await ImageManipulator.manipulateAsync(
uri,
[{ resize: { width: 1000 } }],
{ compress: 0.8, format: ImageManipulator.SaveFormat.JPEG }
);
return file;
};
_renderDoneButton = (count, onSubmit) => {
if (!count) return null;
return <TouchableOpacity title={'Done'} onPress={onSubmit}>
<Text onPress={onSubmit}>Done</Text>
</TouchableOpacity>
}
updateHandler = (count, onSubmit) => {
navigation.setOptions({
title: `Selected ${count} files`,
headerRight: () => _renderDoneButton(count, onSubmit)
});
};
renderSelectedComponent = (number) => (
<View style={styles.countBadge}>
<Text style={styles.countBadgeText}>{number}</Text>
</View>
);
const emptyStayComponent = <Text style={styles.emptyStay}>Empty =(</Text>;
return (
<View style={[styles.flex, styles.container]}>
<ImageBrowser
max={4}
onChange={updateHandler}
callback={imagesCallback}
renderSelectedComponent={renderSelectedComponent}
emptyStayComponent={emptyStayComponent}
/>
</View>
);
};
export default ImageBrowse;
Whenever I press the heart button it updates the state and calls renderHeart method in TutorsCard.js but it also updates every 6th card's state and so on when I scroll down to fetch more profiles so they appear to be liked as well. It might be an issue of shouldComponentUpdate in TutorsCard.js I change shouldComponentUpdate multiple times but it didn't work.
Feed.js
class Feed extends Component {
constructor(props) {
super(props);
this.state = {
name: '',
profiles: new DataProvider((r1, r2) => {
return r1 !== r2;
}),
loading: false,
liked: false,
color: 'red',
refreshing: false,
limit: 10
};
this.layoutProvider = new LayoutProvider((i) => 0, (type, dim) => {
dim.width = width;
dim.height = 250;
})
}
onRefresh = () => {
this.getProfiles();
}
shouldComponentUpdate(nextProps, nextState) {
if (JSON.stringify(this.props) === JSON.stringify(nextProps) &&
this.state === nextState) {
console.log("should cmp update feed false")
return false
}
if (JSON.stringify(this.props) !== JSON.stringify(nextProps) ||
this.state !== nextState) {
console.log("should cmp update feed true")
return true
}
}
onEndReach = async () => {
this.setState({ limit: this.state.limit + 10 }, this.getProfiles)
}
rowRenderer = (type, data) => {
return (
<TutorCard
item={data}
navigation={this.props.navigation}
liked={
this.props.auth.likedProfiles.some((user) => user.id === data._id)
? true
: false
}
/>
);
}
TutorCard.js
class TutorCard extends Component {
state = {
liked: false,
};
shouldComponentUpdate(nextProps, nextState){
if(JSON.stringify(this.props.item._id) === JSON.stringify(nextProps.item._id) &&
this.state.liked === nextState.liked){
console.log("should cmp update tutors card false")
return false
}
if(JSON.stringify(this.props.item._id) !== JSON.stringify(nextProps.item._id) ||
this.state.liked !== nextState.liked){
console.log("should cmp update tutors card true")
return true
}
}
async likedCard() {
//server request
this.setState({ liked: false });
const res = await axios.post(IP_ADDRESS + '/api/removeLikedProfile',
{ id: this.props.item._id });
this.props.fetchCurrentUser()
}
async dislikeCard() {
//server request
this.setState({ liked: true });
const res = await axios.post(IP_ADDRESS + '/api/addLikedProfile',
{ id: this.props.item._id });
this.props.fetchCurrentUser()
}
capitalize = str => {
return str.charAt(0).toUpperCase() + str.slice(1);
};
renderHeart = () => {
if (!this.props.liked && !this.state.liked) {
return (
<TouchableOpacity onPress={() => this.dislikeCard()}>
<AntDesign name="hearto" size={normalize(23)} color="#26a03a" />
</TouchableOpacity>
);
}
if(this.props.liked || this.state.liked){
return (
<TouchableOpacity onPress={() => this.likedCard()}>
<AntDesign name="heart" size={normalize(23)} color="red" />
</TouchableOpacity>
);
}
}
render() {
return (
<View>
<Button style={{
flexDirection: 'row', flex: 1,
justifyContent: 'flex-end'
}}
transparent>{this.renderHeart()}</Button>
</View>
);
}
}
I tried to access all the files in a custom folder I created on my RNCamera roll app to create a gallery with it. In my code I believe i specified the variable "videos", but still am getting a reference error: "can't find variable videos", what do i do to solve it, how will i be able to get rid of the error.... here is my code...
I added constructor in the this.state but still get the same error
constructor() {
super();
this.state = {
modalVisible: false,
videos: [],
index: null
}
}
getPhotos = () => {
CameraRoll.getPhotos({
first: 20,
groupTypes: 'Album',
groupName: 'Custom VideoFolder',
assetType: 'Videos'
})
.then(r => this.setState({ videos: r.edges}))
.then((statResult) => {
let videos = []
var allowedExtensions = /(\.avi|\.mp4|\.mov|\.wmv|\.avi)$/i;
statResult.forEach(item => {
if (item.isFile() && !allowedExtensions.exec(item.originalFilepath)) {
videos.push(item)
}
});
console.log(videos)
})
}
toggleModal = () => {
this.setState({ modalVisible: !this.state.modalVisible})
}
share = () => {
const vocvideo = this.state.videos[this.state.index].node.video.uri
RNFetchBlob.fs.readFile(vocvideo, 'uri')
.then((data) => {
let shareOptions = {
title: "React Native Share Example",
message: "Check out this video!",
url: `data:video/mp4;uri,${data}`,
subject: "Check out this video!"
};
Share.open(shareOptions)
.then((res) => console.log('res:', res))
.catch(err => console.log('err', err))
})
}
render() {
console.log('state :', this.state)
return (
<View style={styles.container}>
<Button
title='View videos'
onPress={() => { this.toggleModal(); this.getPhotos() }}
/>
<Modal
animationType={"slide"}
transparent={false}
visible={this.state.modalVisible}
onRequestClose={() => console.log('closed')}
>
<View style={styles.modalContainer}>
<Button
title='Close'
onPress={this.toggleModal}
/>
<ScrollView
contentContainerStyle={styles.scrollView}>
{
this.state.videos.map((p, i) => {
const isSelected = i === this.state.index;
const divide = isSelected && this.share === true ? 1 : 3;
return (
<Video
style={{opacity: i === this.state.index ? 0.5 : 1, width: width/divide, height: width/divide}}
key={i}
underlayColor='transparent'
onPress={() => this.setIndex(i)}
source={{uri: video}}
/>
)
})
}
</ScrollView>
So I'm trying to make a simple application with expo and expo audio that will generate a list of audio buttons and text. But I cannot figure out how react works regarding redrawing the setState OUTSIDE componentWillMount and how to remake a soundobject with a new URI
So right now it will work but only playing the FIRST uri, I assume this is because the object still exists.
And it will not change the state of the button, I know this is because react cant see its changing for some reason from FlatList
It works outside of it, if I only make one button in renders view.
FlatList will render the setStates if I use LegacyImplementation=true .. But Im warned this is deprecated. And it renders it for all buttons at the same time
This is my handlerClass:
export class TSSGetter extends React.Component {
constructor(props){
super(props);
this.state ={
isLoading: true,
playingStatus: "Play"
}
}
retrieveData() {
const endpoint = 'http://127.0.0.1:3333/get'
const data = {
"userId": "123412341234",
"hmac": "detteerikkeenrigtighmac"
}
return new Promise((resolve, reject) => {
fetch(endpoint, {
method: 'POST',
headers: {
'Accept': 'application/json',
'content-type':'application/json'
},
body: JSON.stringify(data)
})
.then((resp) => {
console.log('hej return')
return resp.json();
})
.then((resp) => {
resolve(resp);
console.log('resp')
}).catch(function(error) {
console.log(error,'naeh')
});
});
}
componentDidMount(){
this.retrieveData()
.then((resp) => {
var pages = resp.books.contentObjects
pages.map((userData) => {
console.log('superduper pages', userData.contentObjectId)
})
this.setState({
isLoading: false,
dataSource: resp.books.contentObjects,
dataroot: resp.books
});
}).catch((err) => {
//handle error
console.log("Api call error2");
alert(err);
})
}
async _playRecording(AudioURL) {
console.log(AudioURL)
const { sound } = await Audio.Sound.createAsync(
{uri: AudioURL},
{
shouldPlay: true,
isLooping: true,
},
this._updateScreenForSoundStatus,
);
this.sound = sound;
this.setState({
playingStatus: 'playing'
});
}
_updateScreenForSoundStatus = (status) => {
if (status.isPlaying && this.state.playingStatus !== "playing") {
this.setState({ playingStatus: "playing" });
} else if (!status.isPlaying && this.state.playingStatus === "playing") {
this.setState({ playingStatus: "donepause" });
}
};
async _pauseAndPlayRecording() {
if (this.sound != null) {
if (this.state.playingStatus == 'playing') {
console.log('pausing...');
await this.sound.pauseAsync();
console.log('paused!');
this.setState({
playingStatus: 'donepause',
});
} else {
console.log('playing...');
await this.sound.playAsync();
console.log('playing!');
this.setState({
playingStatus: 'playing',
});
}
}
}
_syncPauseAndPlayRecording() {
if (this.sound != null) {
if (this.state.playingStatus == 'playing') {
this.sound.pauseAsync();
} else {
this.sound.playAsync();
}
}
}
_playAndPause = (AudioURL) => {
console.log(AudioURL)
switch (this.state.playingStatus) {
case 'Play':
this._playRecording(AudioURL);
break;
case 'donepause':
case 'playing':
this._pauseAndPlayRecording();
break;
}
}
render(){
if(this.state.isLoading){
return(
<View style={{flex: 1, padding: 20}}>
<ActivityIndicator/>
</View>
)
}
const styling = {
flex: 1,
paddingTop:10
// flexDirection: 'row'
}
const data = this.state.dataroot;
return(
<View style={styles.container}>
<FlatList
data={this.state.dataSource}
renderItem={({item}) =>
<View>
<TouchableOpacity style={styles.button} onPress={() => this._playAndPause(item.AudioURL)}>
<Text style={styles.buttonText}>
{this.state.playingStatus}+ {item.contentObjectId}
</Text>
</TouchableOpacity>
<Text style={styles.description}>
{item.text},
</Text>
</View>
}
keyExtractor={(item, index) => item.contentObjectId}
/>
</View>
);
}
}
UPDATE: setting extraData={this.state} in flatlist updates the button.. But all the buttons. How do I change the scope of the button?
You could create a specific component for the items in the FlatList. Each of the items will then have their own state.
import React, { Component } from "react";
import { StyleSheet, Text, View } from "react-native";
import { FlatList } from "react-native-gesture-handler";
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<FlatList
keyExtractor={(item, index) => index.toString()}
data={[1, 2, 3, 4, 5]}
renderItem={({ item }) => <Sound />}
/>
</View>
);
}
}
class Sound extends Component {
constructor() {
super();
this.state = {
status: "IDLE"
};
}
onChangeState = value => {
this.setState({
status: value
});
};
render() {
const { status } = this.state;
return (
<View style={{width: 200,paddingVertical: 10}}>
<Text>Status: {status}</Text>
<View style={{ flex: 1,flexDirection: "row", justifyContent: "space-between" }}>
<Text onPress={() => this.onChangeState("PLAYING")}>PLAY</Text>
<Text onPress={() => this.onChangeState("STOPPED")}>STOP</Text>
<Text onPress={() => this.onChangeState("PAUSED")}>PAUSE</Text>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 100,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center"
}
});
I checked out in the docs, here, and I saw that it will re-render just if you pass the state prop, see this explanations:
By passing extraData={this.state} to FlatList we make sure FlatList itself will re-render when the state.selected changes. Without setting this prop, FlatList would not know it needs to re-render any items because it is also a PureComponent and the prop comparison will not show any changes.
I have implemented a little search functionality in my reactjs application.
The Problem is, that my "searchHandler" function is triggered after every single letter the user enters in the textfield... So e.g. for the term "Lorem" my function is fetching 5 times from my api :(
How can I solve this problem?
Here is my code:
const { scaleDown } = transitions;
function searchingFor(term){
return function(x){
return x.title.toLowerCase().includes(term.toLowerCase()) ||
x.body.toLowerCase().includes(term.toLowerCase());
}
}
class ViewAll extends React.Component{
constructor(props){
super(props);
this.state = {
term: '',
mounted: true,
tracks: [],
hasMoreItems: true,
page: 2,
}
this.searchHandler = this.searchHandler.bind(this);
this.focus = this.focus.bind(this);
this.keyPress = this.keyPress.bind(this);
}
loadContent() {
var requestUrl = this.props.url;
fetch(requestUrl + this.state.page + '&_limit=3').then((response)=>{
return response.json();
}) .then((tracks)=>{
this.setState({ tracks: this.state.tracks.concat(tracks)});
this.setState({page: this.state.page + 1});
if(this.state.page === 6){
this.setState({hasMoreItems: false})
}
}).catch((err)=>{
console.log("There has been an error");
});
}
componentDidMount() {
window.scrollTo(0, 0);
var requestUrl = this.props.url;
fetch(requestUrl + '1&_limit=3')
.then((response)=>{
return response.json();
}) .then((data)=>{
this.setState({tracks : data});
})
.catch((err)=>{
console.log("There has been an error");
});
//this.focus();
}
searchHandler(event){
this.setState({term: event.target.value});
var requestUrl = 'https://questdb.herokuapp.com/all?q='
fetch(requestUrl + this.state.term).then((response)=>{
return response.json();
}) .then((tracks)=>{
this.setState({ tracks: this.state.tracks.concat(tracks)});
}).catch((err)=>{
console.log("There has been an error");
});
}
focus() {
this.textInput.focus();
}
keyPress(e){
if(e.keyCode == 13){
console.log('value', e.target.value);
// put the login here
}
}
render() {
const {term, data, tracks} = this.state;
const loader = <div className="loader2"> </div>;
var items = [];
const imageUrl = require(`../assets/Book.jpg`)
tracks.filter(searchingFor(term)).map(function(title, i)
{
items.push(
<div>
<MuiThemeProvider>
<Paper style={{ borderRadius: "2em",
background: '#ffffff'
}} zDepth={1} >
<ItemViewAll
key={title.id}
/>
</Paper>
</MuiThemeProvider>
</div>
);
}, this);
return (
<div>
<Fade in={true} timeout={1000}>
<div >
<MuiThemeProvider>
<TextField hintText='Bot suchen...'
type="Text"
onChange={this.searchHandler}
value={term}
underlineFocusStyle={{borderColor: '#B00020', borderWidth: 3}}
underlineStyle={{borderColor: '#B00020', borderWidth: 1.5, top: '45px'}}
hintStyle={{fontSize: '8.1vw', fontFamily: 'Anton', color: 'rgba(255,255,255,0.9)'}}
inputStyle={{fontSize: '8.1vw', fontFamily: 'Anton', color: '#ffffff'}}
ref={(input) => { this.textInput = input; }}
style={{caretColor: '#ffffff', width: '90%', maginLeft: 'auto', marginRight: 'auto', marginTop: '12%' }}
InputLabelProps={{ shrink: true }}
/>
</MuiThemeProvider>
</div>
</Fade>
<InfiniteScroll
pageStart={1}
loadMore={this.loadContent.bind(this)}
hasMore={this.state.hasMoreItems}
initialLoad={true}
>
{items}
</InfiniteScroll>
</div>
)
}
}
export default ViewAll;
Here you can check out the Website with the broken search function. As you can see the items are shown double or even triple... After the textfield is emptied, the search results should be removed and only the normal fetched ones should be shown.
https://www.genko.de (use the mobile version in chrome)
Thank you :)
Use lodash debounce. It is used for this exact use case
https://stackoverflow.com/questions/48046061/using-lodash-debounce-in-react-to-prevent-requesting-data-as-long-as-the-user-is
Sample:
import React, {Component} from 'react'
import { debounce } from 'lodash'
class TableSearch extends Component {
//********************************************/
constructor(props){
super(props)
this.state = {
value: props.value
}
this.changeSearch = debounce(this.props.changeSearch, 250)
}
//********************************************/
handleChange = (e) => {
const val = e.target.value
this.setState({ value: val }, () => {
this.changeSearch(val)
})
}
//********************************************/
render() {
return (
<input
onChange = {this.handleChange}
value = {this.props.value}
/>
)
}
//********************************************/
}
If you don't need full lodash package you can write it yourself:
function debounce(f, ms) {
let timer = null;
return function (...args) {
const onComplete = () => {
f.apply(this, args);
timer = null;
}
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(onComplete, ms);
};
}
The first arg (f) is your function which should not be performed more often than
second arg (ms) - amount of ms ). So in your case you can write your handler in next way:
handleChange = debounce((e) => {
const val = e.target.value
this.setState({ value: val }, () => {
this.changeSearch(val)
})
}, 1000) // second arg (1000) is your amount of ms