arr.splice() method deletes wrong item in react native project - javascript

I was making a to-do app in react-native. I made a function that deletes the task on press but when I do press, it deletes the first item on the list, not the one I pressed on.
Here is the code I used:
let id = 0;
const [task, setTask] = useState("");
const [taskList, setTaskList] = useState([
{ name: "Add your first task", id: id++ },
]);
const handleTask = (e) => {
e.preventDefault();
setTaskList([...taskList, { name: task, id: id++ }]);
setTask("");
};
function completeTask(index) {
let itemsCopy = [...taskList];
itemsCopy.splice(index, 1);
setTaskList(itemsCopy);
}
{taskList.map((taskList, index) => {
return (
<View style={styles.wrapper}>
<View style={styles.text}>
<TouchableOpacity key={index} onPress={completeTask}>
<Text> {taskList.name} </Text>
</TouchableOpacity>
</View>
</View>
);
})}

You don't pass index when call completeTask and update state wrond way.
onPress={() => completeTask(index)}
function completeTask(index) {
setTaskList((preTasks) => preTasks.filter((item, i) => i !== index));
}

Related

How to add navigation to different items in a rendered array

I am attempting to press on this pressable button, and navigate to a new page. The tricky bit is that this Pressable item is part of a returned array, as there are multiple of them being rendered each with different data. I want each button to take me to a 'product page', each page being different depending on the button
Here is what i have so far:
The main function
const JobRequestList = () => {
const [data, setData] = useState([]);
useEffect(() => {
returnArray().then(data2 => {
setData(data2);
});
}, []);
if (data.length === 0) {
j = [];
return (
<ScrollView>
<View key={'ERROR'} style={styles.wrapperERROR}>
<Text style={styles.textError}> {'No Current Job Requests'} </Text>
</View>
</ScrollView>
);
} else {
return <ScrollView>{data}</ScrollView>;
}
};
This requests the data, and returns it in a form that can be rendered. It either returns a no object, or an array of items from the below function - This is where my onPress is located, and have no idea how to implement a navigation fnction into it. Please note, i already have my navigation functions setup
const returnArray = async () => {
return queryData().then(() => {
return j.map(x => {
return (
<Pressable
key={x.id}
style={styles['wrapper' + x.data().PD]}
onPress={() => {}}>
<Text style={styles.text}> {x.data().PD} </Text>
<Text style={styles.text}> {x.data().address} </Text>
<Text style={styles.text}> {x.data().time} </Text>
</Pressable>
);
});
});
};
The above function then calls the below
const queryData = async () => {
await firestore()
.collection('Jobs')
.where('driver', '==', 'TBA') //TODO ADD CUSTOMER DISTANCE
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
j.push(doc);
});
});
};
Here is what my navigation functions should be inside this class - Again, which is already setup correctly
const navigation = useNavigation();
navigation.navigate('JobInfo');
Thankyou in advanced!
It is anti-pattern in React to store JSX in component state. React components's rendered UI is a function of state & props. Store the data in state and then render the data mapped to JSX.
Example:
queryData fetches firebase docs & data
const queryData = async () => {
await firestore()
.collection('Jobs')
.where('driver', '==', 'TBA') //TODO ADD CUSTOMER DISTANCE
.get()
.then(querySnapshot => {
const docs = [];
querySnapshot.forEach(doc => {
docs.push({
...doc,
data: doc.data(),
});
});
return docs;
});
};
Apply the navigation logic in the Pressable component's onPress handler when mapping the data state.
const JobRequestList = () => {
const navigation = useNavigation();
const [data, setData] = useState([]);
useEffect(() => {
queryData()
.then(data => {
setData(data);
});
}, []);
return (
<ScrollView>
{data.length
? data.map(el => (
<Pressable
key={el.id}
style={styles['wrapper' + el.data.PD]}
onPress={() => {
navigation.navigate('JobInfo');
}}
>
<Text style={styles.text}> {el.data.PD} </Text>
<Text style={styles.text}> {el.data.address} </Text>
<Text style={styles.text}> {el.data.time} </Text>
</Pressable>
))
: (
<View key={'ERROR'} style={styles.wrapperERROR}>
<Text style={styles.textError}> {'No Current Job Requests'} </Text>
</View>
)
}
</ScrollView>
);
};

Search Funtion in An Array React.js

I have an array of universities
And I need to make a search funtion.
My search implementation works perfectly and finds the university that I type.
However when I select the option, it confuses itself and always select by new index.
For example my array is ["A.T. Still University", "Abilene Christian University", "Abraham Baldwin University", "Academy for Five Element University" ... ]
In here, A.T. Still University is at the 0 index originally. and when I make a search with filter, it creates a new array. I type "Abilene" for example and it brings me a new array with results and "Abilene" becomes at the 0 index. However it doesnt select this one and keeps selecting "A.T. Still University" which has the original 0 index.
Can someone help me how to fix this problem ?
I want to be able to select what I type in search area.
My code is below
const SignupStep5 = forwardRef((props, ref) => {
const dispatch = useDispatch();
const userInfo = useSelector(state => state.profile.user);
const universityList = useSelector(state => state.config.universities);
const isFetchingUniversity = useSelector(state =>
isLoadingSelector([fetchUniversities], state),
);
//State
const [
selectedUniversityId,
setSelectedUniversityId,
selectedUniversityIdRef,
] = useStateRef(null);
const [choosenUniversity, setChoosenUniversity, choosenUniversityRef] =
useStateRef('');
const [choosenUniversityId, setChoosenUniversityId, choosenUniversityIdRef] =
useStateRef('');
useEffect(() => {
if (universityList.length) {
if (props.activeIndex == 5 && userInfo && userInfo.university) {
const index = universityList.findIndex(university =>
isEqual(university, userInfo.university),
);
if (index !== -1) {
setSelectedUniversityId(index);
setChoosenUniversity(userInfo.university);
}
}
}
}, [universityList]);
const renderHeader = () => (
<View>
<View style={styles.modalHeaderContainer}>
<Text style={styles.modalHeaderText}>
{strings.aboutMe.selectUniversity}
</Text>
<Touchable
style={styles.modalHeaderTextContainer}
onPress={() => {
if (selectedUniversityIdRef.current != null) {
setChoosenUniversityId(selectedUniversityIdRef.current);
setChoosenUniversity(
universityList[selectedUniversityIdRef.current],
);
closeBottomSheet();
}
}}>
<Text style={styles.modalHeaderSelectText}>
{strings.common.select}
</Text>
</Touchable>
</View>
<View style={styles.modalHeaderLine}></View>
</View>
);
const renderItem = ({item, index}) => (
<Touchable
style={
selectedUniversityIdRef.current === index
? styles.modalChildContainerSelected
: styles.modalChildContainer
}
onPress={() => handleSelection(index)}>
<Text style={styles.modalChildText}>{item}</Text>
</Touchable>
);
const handleSelection = id => {
const selectedId = selectedUniversityIdRef.current;
if (selectedId === id) setSelectedUniversityId(null);
else setSelectedUniversityId(id);
};
const onOverlayPress = () => {
if (choosenUniversityRef.current) {
const index = universityList.findIndex(
university => university === choosenUniversityRef.current,
);
if (index !== -1) {
setSelectedUniversityId(index);
}
} else if (
selectedUniversityIdRef.current != null &&
!choosenUniversityRef.current
) {
setSelectedUniversityId(null);
}
setModalVisible(false);
};
const [searchField, setSearchField] = useState('');
const searchInputFields = () => {
const filteredUniversity = universityList.filter(value => {
return value.toLowerCase().includes(searchField.toLowerCase());
});
return filteredUniversity;
};
return (
<>
<Portal>
{props.activeIndex == 5 && (
<BottomSheet
ref={bottomSheetRef}
snapPoints={snapPoints}
handleComponent={renderHeader}
backdropComponent={renderBackdrop}
onAnimate={handleSheetChanges}>
<TextInput
onChangeText={value => setSearchField(value)}
value={searchField}
style={{padding: 10, alignSelf: 'center'}}
placeholder="Search"
placeholderTextColor={'gray'}
/>
<BottomSheetFlatList
data={searchField ? searchInputFields() : universityList}
keyExtractor={(item, index) => index.toString()}
renderItem={renderItem}
contentContainerStyle={styles.modalChildStyle}
extraData={selectedUniversityIdRef.current}
/>
</BottomSheet>
)}
</Portal>
</>
);
});
export default SignupStep5;

react native : What is the way to populate the EmergencyRolePicker picker in data with a map function rather than hardcoded?

What is the way to populate the EmergencyRolePicker picker in data with a map function rather than hardcoded?
In my example I am trying to populate the picker but I want to do it with a map loop so I can select something from the picker.
The picker comes from the react-native-paper library.
I would be happy for some help with this issue.
this is my code:
import { List } from 'react-native-paper';
export const ActionsScreen = () => {
const [roleList, setRoleList] = useState([]);
useEffect(() => {
(async () => {
try {
// State table
const emcStateList: EmergencyStateType[] = await EmergencyStateTable.getEmergencyStateList();
console.log('emcStateList:', emcStateList.length);
let items = [];
emcStateList.forEach(function (item, i) {
items.push({ label: item.EmergencyName, value: item.EmergencyCode, key: i });
})
setStateList(items);
// Role table
const emcRoleList: EmergencyRoleType[] = await EmergencyRoleTable.getEmergencyRoleList();
console.log('emcRoleList:', emcRoleList.length);
items = [];
emcRoleList.forEach(function (item, i) {
items.push({ label: item.RoleName, value: item.RoleCode, key: i });
})
setRoleList(items);
//
} catch (error) {
console.log('A problem getting emergency list from db:', error);
}
})();
}, []);
const EmergencyRolePicker = () => {
return (
<List.Accordion title={roleList[0].label} id="0">
<List.Item title={roleList[1].label} />
<List.Item title={roleList[2].label} />
</List.Accordion>
)
}
return (
<>
<SafeAreaView style={styles.container}>
<ScrollView style={styles.scrollView}>
<View style={styles.text}>
<Text style={styles.label}>{MenuStrings.EmergencyRole}</Text>
<View style={styles.pickerView}>
<EmergencyRolePicker />
</View>
</View>
</ScrollView>
</SafeAreaView>
<View style={styles.BottomViewArea}>
<TouchableOpacity
style={{ alignSelf: 'flex-start', marginLeft: 60 }}
onPress={() => {
}}>
<Ionicons
name="send-sharp"
size={30}
color="white"
/>
</TouchableOpacity>
</View>
</>
);
}
this is the data:
[
{
"key":0,
"label":"red",
"value":1
},
{
"key":1,
"label":"green",
"value":2
},
{
"key":2,
"label":"yellow",
"value":3
}
]
Instead of the for loop, using the map as already mentioned by you would look like this:
let items = emcStateList.map((item, i) => ({
label: item.EmergencyName,
value: item.EmergencyCode,
key: i
})
setStateList(items);
As a sidenote, I would not use the index as a key, if the codes are unique, use those.
Also calling the endpoints one after another causes a waterwall. Instead try this:
const emcStateListPromise: Promise<EmergencyStateType[]> = EmergencyStateTable.getEmergencyStateList();
const emcRoleListPromise: Promise<EmergencyRoleType[]> = EmergencyRoleTable.getEmergencyRoleList();
const emcStateList = await emcStateListPromise
const emcRoleList = await emcRoleListPromise
Update:
useEffect(() => {
(async () => {
try {
// State table
const emcStateList: EmergencyStateType[] = await EmergencyStateTable.getEmergencyStateList();
let items = emcStateList.map((item, i) => ({
label: item.EmergencyName,
value: item.EmergencyCode,
key: i
}))
setStateList(items);
// Role table
const emcRoleList: EmergencyRoleType[] = await EmergencyRoleTable.getEmergencyRoleList();
console.log('emcRoleList:', emcRoleList.length);
items = emcRoleList.map((item, i) => ({
label: item.RoleName, value: item.RoleCode, key: i
}))
setRoleList(items);
//
} catch (error) {
console.log('A problem getting emergency list from db:', error);
}
})();
}, []);

How to sort a flatlist in react native [duplicate]

This question already has an answer here:
Sorting react-native FlatList
(1 answer)
Closed 2 years ago.
I am working on a to do list app in react native, when a new item is added it goes directly to the last place and I will like every new object to go to the first place. To achieve this I tried adding a function that is supposed to sort the items but it the code doesnt make any changes. How can I sort these items in my to do list?
app.js
const [todos, setTodos] = useState([]);
const [addMode, setAddMode] = useState(false);
const [isReady, setIsReady] = useState(false);
const addTodoHandler = addTodos => {
if (addTodos.lenght === 0) {
return;
};
setTodos(prevTodos => [...prevTodos, { key: Math.random().toString(), value: addTodos, date: Date.now() }]);
setAddMode(false);
Keyboard.dismiss();
};
const sortTodos = () => { //this is the function that is supposed to sort the items.
const todoSort = [...todos];
const soarted = todoSort.sort((a, b) => {
return a.todoSort - b.todoSort;
})
setTodos(soarted);
};
return (
<View style={styles.screen}>
<Header />
<AddTodo onAddTodo={addTodoHandler} />
<FlatList
keyExtractor={(item, index) => item.key}
data={ todos }
renderItem={({ item }) => <TodoItem key={item.key}
todoKey={item.key}
title={item.value}
editHandler={handleEdit}
pressHandler={pressHandler}/> }
/>
</View>
);
AddTodo.js
const AddTodo = props => {
const [text, setText] = useState('');
const changeHandler = (val) => {
setText(val);
};
const addTodoHandler = () => {
props.onAddTodo(text);
setText('');
};
return (
<View style={styles.inputView}>
<TextInput style={styles.textInput} placeholder='What do you want to do?' onChangeText={changeHandler} value={text}/>
<Buttons title="Add" onPress={addTodoHandler} style={styles.salsachBtn}/>
</View>
);
};
TodoItem.js
const TodoItem = props => {
return (
<View>
<View style={styles.items}>
<View style={styles.itemContainer}>
<Text style={styles.itemText}>{props.title}</Text>
</View>
</View>
</View>
);
};
if you have any questions please let me know in the comments:)
First idea:
Add your 'sortTodos' inside function that handle adding new item.
Add date to items with e.g. Date.now()
Sort a.date - b.date
Second (without sorting): you can try to use unshift
const newTodo = [...prevTodos]
newTodo.unshift({ key: Math.random().toString(), value: addTodos });
setTodos(newTodo)

How to add multiple items to a flatlist using textinput?

i'm trying to add items to a flatlist via textinput. at the moment i can only add one item and when i try to add a second it just updates the first item, although the data from the textinput should appear below the previous textinput. i have found a few instances of a similar kind of problem and i know that i probably need to add something to my code but i just can't figure out what. below is my code. i would be grateful for any kind of help :)
function FlatlistComponent({ }) {
const listItems = [];
const [arrayHolder, setArrayHolder] = React.useState([]);
const [textInputHolder, setTextInputHolder] = React.useState('');
useEffect(() => {
setArrayHolder(listItems)
}, [])
const joinData = () => {
listItems.push({ name : textInputHolder });
setArrayHolder(listItems);
}
const FlatListItemSeparator = () => {
return (
<View
style={{
height: 1,
width: "95%",
backgroundColor: '#00678A',
alignSelf: 'center'
}} />
);
}
// Delete note
deleteNote = id => () => {
const filteredData = arrayHolder.filter(item => item.id !== id);
setArrayHolder({ data: filteredData });
}
return (
<View style={styles.MainContainer}>
<FlatList
data={arrayHolder}
width='100%'
extraData={arrayHolder}
keyExtractor={(item) => item.id}
ItemSeparatorComponent={FlatListItemSeparator}
renderItem={({ item }) => <Text style={styles.item} onPress={deleteNote(item.id)}> {item.name} </Text>}
/>
<TextInput
placeholder='Kirjoita uusi'
onChangeText={data => setTextInputHolder(data)}
style={styles.textInputStyle}
underlineColorAndroid='transparent'
clearTextOnFocus={true}
value={listItems}
/>
<TouchableOpacity onPress={joinData} style={styles.button} >
<Text style={styles.buttonText}> + </Text>
</TouchableOpacity>
</View>
);
}
listItems is always an empty array after component re-rendered, you should concat previous arrayHolder with new item:
const joinData = () => {
setArrayHolder([... arrayHolder, {name: textInputHolder }]);
// or use update function
// setArrayHolder(prev => [...prev, {name: textInputHolder }]);
}

Categories

Resources