thats it i need to get the values from the api before this one loads the slider this is how i call the api
useEffect(() => {
async function BCcontroller() {
const vCreationUser = 6;
const vSolicitudeId = 8;
const { data } = await ForceApi.post(`/ConsultBCController.php`, {vSolicitudeId, vCreationUser});
const values = data.terms;
setpterms(data.terms);
//console.log(values);
const [termA, termB, termC, termD] = values.split(',');
setvA(Number(termA));
setvB(Number(termB));
setvC(Number(termC));
setvD(Number(termD));
// console.log(values);
}
BCcontroller();
}, );
this is the slider code
<View style={{ alignItems: "stretch", justifyContent: "center" }}>
<Slider
maximumValue={D > 0 ? 4 : 3}
minimumValue={1}
step={1}
value={valuesFromApi.indexOf(Value)}
onValueChange={index => setValue(valuesFromApi[index])}
/>
<View style={styles.plazos}>
<Text style={styles.plazo1}>{A} meses</Text>
<Text style={styles.plazo2}>{B} meses</Text>
<Text style={styles.plazo3}>{C} meses</Text>
{D > 0 ? <Text style={styles.plazo3}>{D} meses</Text> : null}
</View>
<Text style={styles.slideText}>Su credito por: ${A}MXN</Text>
<Text style={styles.slideText}>Usted recibe: ${A}MXN</Text>
<Text style={styles.slideText}>A un plazo de: {sliderValue2} meses</Text>
<Text style={styles.PaymentText}>Su pago: ${A}.00 MXN</Text>
</View>
i thougt it was this way but the screen loads with a lot of undefineds and then it get the values of the api, so i want to have the values first and then render te components thanks for your help
You probably want your component to return null when there is no data yet. Only when the data is there, you can return the view+Slider.
Something like this:
const MyComponent = () => {
const [data, setDate] = useState();
useEffect(() => {
// ...
const { data } = await ForceApi.post(`/ConsultBCController.php`, {vSolicitudeId, vCreationUser});
setData(data)
// ...
}, [])
if (!data) return null;
return (
<View style={{ alignItems: "stretch", justifyContent: "center" }}>
// ...
</View>
)
}
When data is there, you call setData which will cause a rerender returning the View+Slider.
Of course the code above is incomplete and untested. It's intended to convey my intention. If it doesn't quite make sense, leave a comment and I'll try to enhance.
Related
I have been trying to create a search bar all day. I finally found this guide which seemed ok: https://blog.jscrambler.com/add-a-search-bar-using-hooks-and-flatlist-in-react-native/. I followed it through using my own API and I am not getting any errors exactly, but the code in this tutorial seems unfinished.
Here is what I have:
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, SafeAreaView, TextInput } from 'react-native';
import { Card, Header } from 'react-native-elements'
import { styles } from './styles.js';
import filter from 'lodash.filter';
const FormsScreen = ({navigation, route}) => {
const [isLoading, setIsLoading] = useState(false);
const [data, setData] = useState([]);
const [query, setQuery] = useState('');
const [fullData, setFullData] = useState([]);
//Fetch all users from database
useEffect(() =>{
setIsLoading(true);
fetch('http://10.0.2.2:5000/forms').then(response =>{
if(response.ok){
return response.json();
}
}).then(data => setFullData(data)).then(setIsLoading(false));
}, []);
function renderHeader() {
return (
<View
style={{
backgroundColor: '#fff',
padding: 10,
marginVertical: 10,
borderRadius: 20
}}
>
<TextInput
autoCapitalize="none"
autoCorrect={false}
clearButtonMode="always"
value={query}
onChangeText={queryText => handleSearch(queryText)}
placeholder="Search"
style={{ backgroundColor: '#fff', paddingHorizontal: 20 }}
/>
</View>
);
}
const handleSearch = text => {
const formattedQuery = text.toLowerCase();
const filteredData = filter(fullData, form => {
return contains(form, formattedQuery);
});
setData(filteredData);
setQuery(text);
};
const contains = ({ ID }, query) => {
console.log("ID was: "+ID);
console.log("Query was: "+query);
const id = ID;
console.log('id was: '+id);
if (id.toString().includes(query)) {
return true;
}
return false;
};
if (isLoading) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<ActivityIndicator size="large" color="#5500dc" />
</View>
);
}
else{
return (
<SafeAreaView>
<Header
leftComponent={{ icon: 'menu', color: '#fff' }}
centerComponent={{ text: 'Request Forms', style: { color: '#fff', fontSize: 25} }}
rightComponent={{ icon: 'home', color: '#fff' }}
/>
<FlatList
ListHeaderComponent={renderHeader}
keyExtractor={(item) => item.ID.toString() }
data={fullData}
renderItem={({item}) => (
<Card>
<Card.Title>{item.ID}</Card.Title>
<Card.Divider/>
<View style={styles.Container}>
<Text>{item.Comments}</Text>
{/* <Image source={require('./System apps/Media Manager/Gallery/AppPhotos/45e5cefd-7798-4fe9-88de-86a0a15b7b9f.jpg')} /> */}
<Text>{item.RoadName}</Text>
</View>
<View style={styles.ListContainer}>
<Text style={styles.LabelText}>Name</Text>
<Text style={styles.LabelText}>Phone</Text>
<Text style={styles.LabelText}>Email</Text>
</View>
<View style={styles.ListContainer}>
<Text style={styles.CardText}>{item.Name}</Text>
<Text style={styles.CardText}>{item.Phone}</Text>
<Text style={styles.CardText}>{item.Email}</Text>
</View>
</Card>
)}
/>
</SafeAreaView>
);
}
}
export default FormsScreen;
I have 2 main problems here.
1.) The tutorial had me initialize data and setData. setData is called and it looks to me like that is the final result after the search. The problem is that the author never actually used the variable data so I what do I do with it? Right now the the list is unaltered no matter what happens.
2.) I know he is using a different API so instead of filtering through First name, Last name, and Email I am only searching through ID. In this section of the tutorial:
const contains = ({ name, email }, query) => {
const { first, last } = name;
if (first.includes(query) || last.includes(query) || email.includes(query)) {
return true;
}
return false;
};
How does this code relate first and last to the first and last name values in the data? When I use this code but substitute name with ID and therefor first with id the value of query is correct (19 for example) but the value of ID is 2040 which is not the value I am looking for, but 2040 is the last ID in the database, or in other words the most recently entered row.
This is a sample of my data for reference:
Any help is greatly appreciated.
Please update
data={fullData}
to
data={query ? data : fullData} in flat list props. That should display your filtered data whenever search query updated.
How do I make the text "BIRD" label that will check the checkbox while press on it ?
According to my example it does not work and it is not clear to me.
import CheckBox from '#react-native-community/checkbox';
function App(props) {
const isSelected = useSelector((rootState) => rootState.BitzuaDigumReducer.checkBox);
return (
<TouchableOpacity
activeOpacity={1}
style={{
flexDirection: 'row',
alignSelf: 'flex-start',
top: 20,
left: 10,
}}
>
<CheckBox
value={isSelected}
onValueChange={(value) => dispatch(setCheckBox(value))}
style={styles.checkbox}
tintColors={{ true: 'white', false: 'white' }}
/>
<Text style={styles.label}>BIRD</Text>
</TouchableOpacity>
);
}
I mocked Checkbox on this example, but please review it. It should give you a basic idea on how to toggle checkbox value by pressing the label: https://snack.expo.io/#zvona/toggle-checkbox-on-label
Core code:
const App = () => {
const [isSelected, setSelected] = useState(false);
const toggleCheckbox = () => {
setSelected(!isSelected);
// Here you can dispatch the event to state management
};
return (
<View style={styles.container}>
<Checkbox onValueChange={toggleCheckbox} selected={isSelected} />
<TouchableOpacity activeOpacity={0.8} onPress={toggleCheckbox}>
<Text style={styles.label}>{'Label'}</Text>
</TouchableOpacity>
</View>
);
};
After getting data from API I set it to state, and render items in Flatlist,
when I select any item from it I manipulate data and add a new property to item object named as "toggle: true"
and it's works well when I select any item from list I add a border based on toggle,
But when I go back to previous screen then re open the lists screen I can see the border rendered around the items, although I reset the state when the unmounted screen
So what's the wrong I made here?
Code snippet
Data
export default {
...
services: [
{
id: 0,
name: 'nameS0',
logo:
'https://cdn2.iconfinder.com/data/icons/hotel-98/64/hair-dryer-tools-beauty-hairdressing-512.png',
price: 19.99,
},
],
employees: [
{
id: 0,
name: 'name0',
img:
'https://www.visualelementmedia.com/wp-content/uploads/2015/04/person-4-400x629.jpg',
},
...
],
};
const VendorProfile = ({navigation}) => {
const [services, setServices] = React.useState(null);
const [employees, setEmployees] = React.useState(null);
const [serviceSelected, setServiceSelected] = React.useState(null);
const [employeeSelected, setEmployeeSelected] = React.useState(null);
// For selected Item (services, employees)
const itemSelected = (data, id) => {
const updated = data.map((item) => {
item.toggle = false;
if (item.id === id) {
item.toggle = true;
data === services
? setServiceSelected(item)
: setEmployeeSelected(item);
}
return item;
});
data === services ? setServices(updated) : setEmployees(updated);
};
...
const renderEmployees = ({item}) => {
return (
<TouchableOpacity
onPress={() => itemSelected(employees, item.id)}
delayPressIn={0}
style={styles.employeeContainer}>
<EmployeePattern style={{alignSelf: 'center'}} />
<View style={styles.employeeLogo}>
<Image
source={{uri: item.img}}
style={[styles.imgStyle, {borderRadius: 25}]}
/>
</View>
<View style={{marginTop: 30}}>
<Text style={{textAlign: 'center'}}> {item.name}</Text>
</View>
<View style={{marginTop: 10, alignSelf: 'center'}}>
{item.toggle && <AntDesign name="check" size={25} color="#000" />} // here it's stuck after back and reopen the screen
</View>
</TouchableOpacity>
);
};
React.useEffect(() => {
setServices(VendorProfileData.services);
setEmployees(VendorProfileData.employees);
() => {
setServices(null);
setEmployees(null);
};
}, []);
return (
<View style={styles.container}>
<FlatList
data={services}
renderItem={renderServices}
horizontal
keyExtractor={(item) => item.id.toString()}
contentContainerStyle={{
justifyContent: 'space-between',
flexGrow: 1,
}}
/>
.....
</View>
);
};
Ok so after trying multiple times, i got it
change this
const updated = data.map((item) => {
to this
const updated = data.map((old) => {
let item = {...old};
and please make sure everything is working and we didn't break a thing :),
On your ItemSelected function you are passing the whole employees list, and going through it now thats fine, but when you changing one item inside this list without "recreating it" the reference to that item is still the same "because its an object" meaning that we are modifying the original item, and since we are doing so, the item keeps its old reference, best way to avoid that is to recreate the object,
hope this gives you an idea.
I am not sure how to add a delete function in a FlatList. I know I can make different components, but I want to know how to do it within this one file. I've trying to figure this out for hours, but do not know how to do.
export default function test() {
const [enteredGoal, setEnteredGoal] = useState("");
const [courseGoals, setCourseGoals] = useState([]);
const goalInput = enteredText => {
setEnteredGoal(enteredText);
};
const addGoal = () => {
setCourseGoals(currentGoals => [
...currentGoals,
{ key: Math.random().toString(), value: enteredGoal }
]);
};
const removeGoal = goalId => {
setCourseGoals(currentGoals => {
return currentGoals.filter((goal) => goal.id !== goalId);
})
}
return (
<View style={styles.container}>
<View>
<TextInput
color="lime"
style={styles.placeholderStyle}
placeholder="Type here"
placeholderTextColor="lime"
onChangeText={goalInput}
value={enteredGoal}
/>
</View>
<FlatList
data={courseGoals}
renderItem={itemData => (
<View style={styles.listItem} >
<Text style={{ color: "lime" }}>{itemData.item.value}</Text>
</View>
)}
/>
<View>
<TouchableOpacity>
<Text style={styles.button} onPress={addGoal}>
Add
</Text>
</TouchableOpacity>
</View>
</View>
);
}
You just need to modify your code a bit to handle the delete button. Since you already have delete functionality, call that function when you click the delete button. That's it.
<FlatList
data={courseGoals}
renderItem={itemData => (
<View style={{ flexDirection: "row", justifyContent: "space-between" }}>
<Text style={{ color: "lime" }}>{itemData.item.value}</Text>
<TouchableOpacity onPress={() => removeGoal(itemData.item.key)}>
<Text>Delete</Text>
</TouchableOpacity>
</View>
)}
/>;
EDIT
change your removeGoal function as below
const removeGoal = goalId => {
setCourseGoals(courseGoals => {
return courseGoals.filter(goal => goal.key !== goalId);
});
};
Hope this helps you. Feel free for doubts.
I have a flat list, which gets its data source as a state. Actually, this data is from firebase, and i have been using redux. So, the data is fetched in the actions, and using callback i get the data to state.
What i want to achieve is, when there is no data found from the api, An empty list message should be show in the view. Actually , i achieved this using "ListEmptyComponent". But whats happening is the screen starts with empty message, and the spinner loads below it, and then if data found the message goes away as well as spinner.
But, what i wanted is, when the view gets rendered the first thing everyone should see is the spinner, and then if data empty spinner hides then empty list message displays.
How to achieve this ?
My Action :
export const fetchOrderHistory = (phone, callback) => {
return (dispatch) => {
dispatch({ type: START_SPINNER_ACTION_FOR_ORDER_HISTORY })
firebase.database().ref('orders/'+phone)
.on('value', snapshot => {
const snapShotValue = snapshot.val();
callback(snapShotValue);
dispatch ({ type: ORDER_HISTORY_FETCHED , payload: snapshot.val()});
dispatch({ type: STOP_SPINNER_ACTION_FRO_ORDER_HISTORY })
});
};
};
My Flat List & spinner:
<FlatList
data={this.state.historyOfOrders}
keyExtractor={item => item.uid}
ListEmptyComponent={this.onListEmpty()}
renderItem={({ item }) => (
<Card
containerStyle={{ borderRadius: 5 }}
>
<View style={styles.topContainerStyle}>
<View>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('ViewOrderScreen', {itemsOfOrder: item}) }
>
<View style={styles.viewOrderContainer}>
<View style={styles.viewOrderTextContainer}>
<Text style={styles.viewOrderTextStyle}>View Order</Text>
</View>
<Icon
name='ios-arrow-forward'
type='ionicon'
color='#ff7675'
/>
</View>
</TouchableOpacity>
</View>
</View>
</View>
</Card>
)}
/>
{this.props.isSpinnerLoading &&
<View style={styles.loading}>
<ActivityIndicator size="large" color="#03A9F4"/>
</View> }
My Call back at componentWillMount which set state:
componentWillMount() {
this.props.fetchOrderHistory((this.props.phone), (snapShotValue)=> {
const userOrderHistory = _.map(snapShotValue, (val,uid) => ({uid, ...val}))
this.setState({ historyOfOrders: userOrderHistory })
});
}
My EmptyList Message:
onListEmpty = () => {
return <View style={{ alignSelf: 'center' }}>
<Text style={{ fontWeight: 'bold', fontSize: 25 }}>No Data</Text>
</View>
}
My State:
state = { historyOfOrders: "" }
I am getting the spinner values from the reducers, using mapStateToProps.
Kindly Guide me, through
you have to do two things for that.
First, show Flatlist only if the loader is stopped. Second, set default value of this.state.historyOfOrders is null and check if this.state.historyOfOrders not null then only show Flatlist.
Here is a code:
{(!this.props.isSpinnerLoading && this.state.historyOfOrders != null) ?
(
<FlatList
data={this.state.historyOfOrders}
keyExtractor={item => item.uid}
ListEmptyComponent={this.onListEmpty()}
renderItem={({ item }) => (
<Card containerStyle={{ borderRadius: 5 }}>
<View style={styles.topContainerStyle}>
<View>
<TouchableOpacity onPress={() => this.props.navigation.navigate('ViewOrderScreen', {itemsOfOrder: item}) }>
<View style={styles.viewOrderContainer}>
<View style={styles.viewOrderTextContainer}>
<Text style={styles.viewOrderTextStyle}>View Order</Text>
</View>
<Icon
name='ios-arrow-forward'
type='ionicon'
color='#ff7675'
/>
</View>
</TouchableOpacity>
</View>
</View>
</Card>
)}
/>
) : null
}
With this condition, even if you want loader above Flatlist you can do that.
The path you should take is rendering only the spinner when the loading flag is set and rendering the list when loading flag is false.
Your render method should be like below
render()
{
if(this.props.isSpinnerLoading)
{
return (<View style={styles.loading}>
<ActivityIndicator size="large" color="#03A9F4"/>
</View> );
}
return (/** Actual List code here **/);
}