How to make react-native-banner-carousel load images from API(asynchronous) - react-native

I'm using https://github.com/f111fei/react-native-banner-carousel/
It works fined with hardcoded images path.
But this error happened if my images array is empty. It will show error as this image
I guess it caused by empty array (please correct me if im wrong). The state.carousels yet to loading to state when it render.
How can I make it asynchronous, so it can load the images dynamically.
So this is my code.
Dashboard.js
componentWillMount(){
this.props.carouselFetch();
}
renderPage(image, index) {
return (
<View key={index}>
<ImageFluid
source={{ uri: image }}
originalWidth={ 2500 }
originalHeight= { 1000 }
/>
</View>
);
}
render(){
const images = this.props.carousels;
return(
......
<Carousel
autoplay
autoplayTimeout={5000}
loop
index={0}
showsPageIndicator={ false }
pageSize={BannerWidth}
>
{ images.map((image, index) => this.renderPage(image, index))}
</Carousel>
......
);
}
const mapStateToProps = (state) => {
const carousels = state.carousel;
return { carousels };
};
CarouselActions.js
export const carouselFetch = () => {
return (dispatch) => {
fetch('API json')
.then((response) => response.json())
.then((response) => {
if (response.Status === 'Fail') {
return Promise.reject(response)
}
return response
})
.then(carousels => {
carouselFetchSuccess(dispatch, carousels);
})
.catch(() => console.log("Error"));
};
};
const carouselFetchSuccess = (dispatch, carousels) => {
dispatch({
type: CAROUSEL_FETCH_SUCCESS,
payload: _.map(carousels.data, i => i.image_path)
});
};
My Sample API json
The package required sample array method

render(){
const images = this.props.carousels;
if (!images || images.length === 0) {
return null;
}
return(
......
<Carousel
autoplay
autoplayTimeout={5000}
loop
index={0}
showsPageIndicator={ false }
pageSize={BannerWidth}
>
{ images.map((image, index) => this.renderPage(image, index))}
</Carousel>
......
);
}
Don't render carousel when the image list length is 0.

First use :
const images = ( this.props.carousels || [] ).map( (image) => ( {
value: carousels .name,
label: carousels .id,
order: carousels .data.
} );
You have a simple array of objects not an nested array, your response it's a simple res.data and not a res.carousel.data, if you use console.log(res) you will see your array, check that.

Related

React Native dynamic search with flatlist from API

function ManageData({props, navigation}) {
const [details, setDetails] = useState({
dataList: [],
loading: true,
offset: 1,
totalRecords: 0,
search: '',
});
useEffect(() => {
getData();
}, []);
const getData = async () => {
try {
// console.log('search',details.search);
var params = {};
params = {
'pagination[page]': details.offset,
'pagination[perpage]': 10,
};
if(details?.search?.length > 0){
params['query[search]'] = details?.search;
params['pagination[pages]'] = 30;
params['pagination[total]'] = 293;
}else{
params['query'] = ""
}
const result = await getPayeeDetails(session, params);
// console.log('result',result?.data?.data?.length);
if (result?.data?.data?.length > 0) {
setDetails(prev => ({
...prev,
offset: prev.offset + 1,
dataList: [...prev.dataList, ...result.data.data],
loading: false,
totalRecords: result.data.recordsFiltered,
}));
}
} catch (error) {
console.log('getPayeesError', error);
}
};
const loadMore = () => {
try {
if (details.dataList.length != details.totalRecords) {
setDetails(prev => ({
...prev,
loading: true,
}));
getData();
}
} catch (error) {
console.log('LoadMoreError', error);
}
};
const searchHandler=(data)=>{
try{
console.log('clearData',data);
setDetails(prev => ({
...prev,
dataList:[],
offset:1,
search: data == 'RESET'?"":data,
}));
getData();
}catch(error){
console.log("SearchError",error)
}
}
return (
<BackDropContainer
searchHandler={searchHandler}>
<View style={{backgroundColor: 'white', flex: 1}}>
<FlatList
style={{marginTop: '4%'}}
data={details?.dataList}
renderItem={({item}) => (
<TouchableOpacity onPress={() => showDialog(item)}>
<Item data={item} />
</TouchableOpacity>
)}
onEndReached={loadMore}
keyExtractor={(item, index) => index}
/>
</View>
</BackDropContainer>
);
}
I have a flatlist with searchview in my React Native application. Each time user scrolls to the end of flatlist the loadmore function will be called and also the offset value is increased as 1 to fetch next page from API.
Every time the API results array of 10 data from API so the flatlist will be loaded 10 by 10 for each scroll. When I type some data in searchview the searchHandler function will be called, and there I want to reset the offset as 1 and also need to send typed data to the API.
The issue is searched data and offset is not sending with API whenever I try to search the data. State is not updating properly when searching data.
Note: The data which is types has to be sent along with API whenever user search something.

React Native useState Render Error : [Too many re-renders.]

This is my code:
export default function App() {
const [onProcess, setOnProcess] = useState("normal")
var myid = "123"
const [data, setData] = useState([]);
useEffect(() => {
fetch('https://api.npoint.io/0294bea2185268c9ac70')
.then((response) => response.json())
.then((json) => setData(json))
.catch((error) => console.log('ERR :', error))
},[]);
for (let x in data) {
if (data[x].client_id == myid) {
var set = data[x].situation
setOnProcess(set)
console.log(data[x].situation)
break
}
}
const rt_normal = (
<View style={styles.container}>
<Text> This is normal view </Text>
</View>
)
const rt_process = (
<View style={styles.container}>
<Text> This is process view </Text>
</View>
)
if (onProcess == "normal") {
return rt_normal
}
else if (onProcess == "_on_process") {
return rt_process
}
}
The error I got is:
:[Render Error. Too many re-renders. React limits the number of renders to prevent an infinite loop.]
This happens because of setOnProcess(set) code. How can I solve this?
You should remove your for...in loop and refactor to utilise useEffect.
useEffect(() => {
// Get a specific entry where client_id matches myId.
const filteredItem = data.find(item => item.client_id === myId);
// Perform a check as .find() can return undefined.
if(filteredItem.situation) {
setSituation(filteredItem.situation);
}
}, [data]);
Put the for loop inside a useEffect
(Untested) example:
useEffect(() => {
for (let x in data) {
if (data[x].client_id == myid) {
var set = data[x].situation;
setOnProcess(set);
console.log(data[x].situation);
break;
}
}
}, [data]);

React Admin Change field based on related record field

Let's say I have a record called 'assets' which has a column called deducitble. An asset can have one Insurer. The insurer has a boolean field 'allowOtherDeductible'.
When editing the asset, I want the ability to first check if the associated insurer has allowOtherDeductible set to true. If so I'll allow a TextInput for deductible, if false, a SelectInput.
How can I achieve this? I cannot see a way to fetch related record's fields when conditionally rendering fields.
I ended up pulling in all the insurers and loading the asset when the component loaded. Seems a bit inefficient, but I couldn't come up with a better way:
export const AssetEdit = props => {
const dataProvider = useDataProvider();
const [insurers, setInsurers] = useState([]);
const [asset, setAsset] = useState(null);
const [otherDeductible, setOtherDeductible] = useState(false);
useEffect(() => {
dataProvider.getOne('assets', { id: props.id })
.then(({ data }) => {
setAsset(data);
return dataProvider.getList('insurers', { pagination: { page: 1, perPage: 100 } });
})
.then(({ data }) => setInsurers(data))
.catch(error => {
console.log(error)
})
}, [])
useEffect(() => {
if (asset && insurers) {
const selectedInsurer = insurers.find(insurer => insurer.id === asset?.insurer_id);
setOtherDeductible(selectedInsurer?.other_deductible);
}
}, [insurers])
return (
<Edit {...props}>
<SimpleForm>
{otherDeductible &&
<NumberInput
source={'deductible'}
parse={val => dollarsToCents(val)}
format={val => centsToDollars(val)}
/>
}
<FormDataConsumer>
{({ formData, ...rest }) => {
if(!otherDeductible){
return <SelectInput
source="deductible"
parse={val => dollarsToCents(val)}
format={val => centsToDollars(val)}
choices={getDeductibleChoices(formData.insured_value)}
/>
}
}}
</FormDataConsumer>
</SimpleForm>
</Edit>
)
}
I'd write a custom Input taking advantage of the fact that SimpleForm injects the record to all its children:
const DeductibleInput = ({ record }) => {
if (!record) return null;
const { data, loaded } = useGetOne('insurers', record.insured_id);
if (!loaded) return null; // or a loader component
return data.otherDeductible
? <NumberInput
source="deductible"
parse={val => dollarsToCents(val)}
format={val => centsToDollars(val)}
/>
: <SelectInput
source="deductible"
parse={val => dollarsToCents(val)}
format={val => centsToDollars(val)}
choices={getDeductibleChoices(record.insured_value)}
/>
}

To save the data onpress of item in a asycstorage in react native

I'm trying to display the time zone in another screen when an item is pressed in previous flatlist. My data is coming from autocomplete when I'm selecting it is displayed in flatlist.
<Autocomplete
autoCapitalize="none"
autoCorrect={false}
containerStyle={styles.autocompleteContainer}
data={autotime.length === 1 && comp(query, autotime[0].name) ? [] : autotime}
defaultValue={this.state.timeZone}
onChangeText={text => this.setState({ query: text })}
placeholder="Enter Location"
renderItem={({ name, release_date }) => (
<TouchableOpacity onPress={() => this.setState({ query: name,timezoneArray:autotime[0].timezones })}>
<Text style={styles.itemText}>
{name}
</Text>
</TouchableOpacity>
)}
/>
<View style={styles.descriptionContainer}>
{autotime.length > 0 ? (
<FlatList
style={{flex:1}}
data={this.state.timezoneArray}
renderItem={({ item }) => <TimeZoneItem text={item} />}
/>
) : (
<Text style={styles.infoText}>Enter Location</Text>
)}
I want that when I press the items of flatlist it is displayed on another page.
Picture below shows what I have made:
My Data base helper class is:
export const SaveItem = (key, value) => {
AsyncStorage.setItem(key, value);
};
export const ReadItem = async (key) => {
try {
var result = await AsyncStorage.getItem(key);
return result;
} catch (e) {
return e;
}
};
export function MultiRead(key, onResponse, onFailure) {
try {
AsyncStorage.multiGet(key).then(
(values) => {
let responseMap = new Map();
values.map((result, i, data) => {
let key = data[i][0];
let value = data[i][1];
responseMap.set(key, value);
});
onResponse(responseMap)
});
} catch (error) {
onFailure(error);
}
};
export async function DeleteItem(key) {
try {
await AsyncStorage.removeItem(key);
return true;
}
catch (exception) {
return false;
}
}
and here i have added my code to save
handleTimezone = (text) => {
this.setState({ TimeZoneItem: text })
}
newData.TimeZoneItem = this.state.TimeZoneItem
this.setState({
TimeZoneItem: '',
})
ReadItem('timeData').then((result) => {
let temp = []
if (result != null) {
temp = JSON.parse(result)
} else {
temp = []
}
temp.push(newData)
SaveItem('timeData', JSON.stringify(temp))
console.log(`New Data: ${JSON.stringify(temp)}`)
}).catch((e) => {
})
}
<FlatList
style={{flex:1}}
data={this.state.timezoneArray}
renderItem={({ item }) => (
<TouchableOpacity>
<TimeZoneItem text={item} onPress={() => this.props.onPress()}
value={this.state.TimeZoneItem}
/>
</TouchableOpacity>)}
You'll need an array to saved all the saved items.
for example
state = {
saved: []
};
On press the time zone item, get the values from state, add the new item to the array and save the array to async storage using JSON.stringify()
onSave = item => {
const { saved } = this.state;
const newItems = [...saved, item];
this.setState({
saved: newItems
});
const items = JSON.stringify(newItems);
SaveItem("saved", items)
.then(res => {
console.warn("saved", res);
})
.catch(e => console.warn(e));
};
Then in your other screen get the items in using your ReadItem function like.
state = {
saved: []
};
componentDidMount = () => {
ReadItem("saved")
.then(res => {
if (res) {
const saved = JSON.parse(res);
this.setState({
saved: saved
});
}
})
.catch(e => console.warn(e));
};
Working Demo

React Native / Using strings as data in FlatList?

I have a JSON with the following shape for ~50 _source entries:
{
"hits": [
{
"_source": {
"name": "henry",
"jobs": ["judge", "lawyer"]
}
},
{
"_source": {
"name": "henry",
"jobs": ["dev", "waitress"]
}
}
// ...
]
}
Thanks to the community's help, I extracted each jobs as below:
const result = hits.reduce((acc, item) => acc = [item._source.jobs[0], ...acc], [])
console.log(result) // this is an array
I extracted each item from result to add a string (for example "welcome judge"):
for(i in result)
{
var message = 'welcome'+ result[i] //this is a string
}
So now, I want to use a flatlist to render my message:
constructor() {
super()
this.state = { dataSource:'' }
}
componentDidMount() {
fetch('uri')
.then(response => response.json())
.then(json => {
const result = hits.reduce((acc, item) => acc = [item._source.jobs[0], ...acc], []) // this is an array
for(i in result)
{
var message = 'welcome'+ result[i] //this is a string
}
this.setState({ dataSource : messsage})
}
renderItem =({item}) => {
return(
<View>
<Text>item</Text>
</View>)
}
render() {
return (
<View>
<FlatList
data= {[this.state.dataSource]}
renderItem= {this.renderItem}
/>
</View>
);
}
I got only one message (and not my list) and the warning 'missing key for item'
You should have keyExtractor={(x, i) => i.toString()} in your flatlist.
<FlatList
data= {[this.state.dataSource]}
keyExtractor={(x, i) => i.toString()}
renderItem= {this.renderItem}
/>
Here is a FlatList keyExtractor definition.
You will only get a single message, because your input data is just a string (converted to an array in render() to fit specifications). You change your single string variable in every iteration and update with the last changed one. You do need to push every string into an array, before you continue to the next item in the Iterable.
constructor() {
super()
this.state = { dataSource: [] }
}
componentDidMount() {
fetch('uri')
.then(response => response.json())
.then(json => {
// Get all jobs in a single array
const results = hits.reduce((acc, item) => acc = [item._source.jobs[0], ...acc], []);
// Iterate over results, concatenate with 'welcome' and push into a new array
let messages = [];
for(i in result)
{
let message = 'welcome'+ result[i];
messages.push(message);
}
// Update state with the new array 'messages'
this.setState({ dataSource : messages })
}
renderItem = ({ item }) => {
return(
<View>
<Text>{item}</Text>
</View>
);
}
render() {
return (
<View>
<FlatList
data={this.state.dataSource}
keyExtractor={(x, i) => i.toString()}
renderItem= {this.renderItem}
/>
</View>
);
}
Because your data source contains single string. Here you are updating the message var on each iteration, so it will have just the last string of the result array with 'hello' prepended.
for(i in result)
{
var message = 'welcome'+ result[i]
}
You should do something like this
componentDidMount() {
fetch('uri')
.then(response => response.json())
.then(json => {
const result = hits.reduce((acc, item) => acc = [item._source.jobs[0], ...acc], [])
let messages=[];
for(i in result)
{
messages.push('welcome '+ result[i]); //push each element in array
}
this.setState({ dataSource : messsages})
}
Use key extractor to remove missing key warning
render() {
return (
<View>
<FlatList
data= {[this.state.dataSource]}
renderItem= {this.renderItem}
keyExtractor={(item, index) => item + index}
/>
</View>
);
}