How to delete item from an array by onPress event in react - reactjs

I'am making a todo app. I want to call a function on a button Press event and want to pass item id to it.
import React ,{useState} from 'react';
import { StyleSheet, Text, View, TextInput, Button, ScrollView, Alert, FlatList} from 'react-native';
export default function App() {
const [goal,setgoal] = useState('');
const [addInput, setInput] = useState([]);
const changetext= ()=>{
setInput(addInput=>[...addInput,{id: Math.random().toString(), value: goal}]);
};
const texthandler= (enteredText)=>{
setgoal(enteredText);
};
const deleteText = (e)=>{
setInput((addInput)=>addInput.filter(todo=>todo.id !=e.target.id))
}
return (
<View style={styles.screen} >
<View style={styles.InputView}>
<TextInput placeholder="Course Goal" onChangeText={texthandler} value={goal}
style={styles.TextInputStyle}/>
<Button title='ADD' onPress={changetext}/>
</View>
<FlatList data={addInput} keyExtractor={(item,index)=>item.id} renderItem={
(itemData)=>(
<View style={styles.recordList}>
<Text style={styles.Textoutput} id={itemData.item.id} onPress={deleteText}>{itemData.item.value}</Text>
<Button title="Delete" style={styles.DeleteButton} value={itemData} onPress={deleteText} ></Button>
</View>)}/>
</View>
);
}
here i want Delete button to remove respective element from 'addInput' list. same thing is happening by pressing Text field itself.
but here i can pass id from text field but not from button. why so?
how to get it done by using button.
also, should i use 'this' keyword? can we do it without it, because some time it looks confusing to me at initial stage.
thanks in advance

Try this:
const deleteText = (itemID)=>{
setInput(()=>addInput.filter(todo=>todo.id !=itemID))
}
return (
<View style={{marginTop:30}}>
<View>
<TextInput placeholder="Course Goal" onChangeText={texthandler} value={goal}/>
<Button title='ADD' onPress={changetext}/>
</View>
<FlatList data={addInput} keyExtractor={(item,index)=>item.id} renderItem={
(itemData)=>(
<View>
<Text id={itemData.item.id} onPress={()=>deleteText(itemData.item.id)} >{itemData.item.value}</Text>
<Button title="Delete" value={itemData} onPress={()=>deleteText(itemData.item.id)} ></Button>
</View>)}/>
</View>

Well i am not sure about the answer in terms of React-native but in React i would try something like this:-
onPress= { this.deleteText.bind(this,idx) }
If you are not passing this function as a prop, simply remove bind and call it using this.deleteText(idx)

In react/react-native, you can make a event handler method and can handle array items for example like this:
handleOnPress = value => {
let {mArray} = this.state
if(mArray.includes(value)){
let index = mArray.findIndex((item => item==value))
mArray.splice(index, 1)
}else mArray.push(value)
this.setState({mArray})
}
In Your case, have to just delete item from the array so you can just use splice method. for example:
handleOnDelete = item => {
let {mArray} = this.state;
let index = mArray.findIndex((item => item==value))
if(index > -1) mArray.splice(index, 1)
else console.log('item not found')
}

const deleteText = e => setInput(
addInput => addInput.split(addInput.findIndex(todo => todo.id == e.target.id), 1)
)

Related

Passing a button select choice back to previous screen

So after some research, I have learned how to make a button that will take the user to another screen, and provide them a text input where they can enter some words, then on pushing the done button take them back to the previous screen where what they typed will be displayed. But for my particular needs, I am trying to figure out how to instead of a text input have a selection of buttons, such as "large, medium, small" and have that button select the data that would be displayed instead, and return them to the previous page where it is displayed.
initial screen
function HomeScreen( route ) {
navigation = useNavigation();
React.useEffect(() => {
if (route.params?.post) {
}
}, [route.params?.post]);
return (
<View>
<Pressable
title="Create post"
onPress={() => navigation.navigate('CreatePost')}
>
<Text style={{ margin: 10 }}>Post: {route.params?.post}</Text>
</Pressable
</View>
);
}
button selection screen
function CreatePostScreen( route ) {
const navigation = useNavigation();
const [postText, setPostText] = React.useState('');
return (
<>
<Pressable
title="Done"
onPress={() => {
navigation.navigate({
name: 'Home',
params: { postText },
merge: true,
});
}}
>
<Text>
Large
</Text>
</Pressable>
</>
);
}
any insight is greatly appreciated.
You can pass the data in form of object
{post: postText,
buttonType: 'medium'}
For getting the data
React.useEffect(() => {
if (route.params?.post) {
var buttonType= route.params?.buttonType
}
}, [route.params?.post]);
You can store the button type in a variable or state
var buttonType = route.params?.buttonType
Or You can try with useState() hooks
const [buttonType, setButtonType]=useState("")
setButtonType(route.params?.buttonType)
The for using it just do the following
<Text>{buttonType}</Text>
Please follow the React-Documentation

How to use useEffect inside a View in React Native

I want to use an useEffect inside a return (inside a Text that is inside multiple View to be exact) and from what I know, I must use {...} in order to say that what I write is some code. Howether I got a blank screen without errors and I don't know where is the issue with my code.
Here is the code:
const [pass, setPass] = useState(0);
...
return (
<View>
<FlatList
data={letter.description}
numColumns={2}
keyExtractor={(_, index) => index.toString()}
renderItem={({ item }) => {
if (pass >= letter.description?.length) {
useEffect(() => {
setPass((prev) => 0);
});
}
return (
<View>
<Text>
{letter.data[pass]}
{"\n"}
</Text>
<Text>
{letter.description[pass]}
{useEffect(() => {
setPass((prev) => prev + 1);
})}
{"\n"}
</Text>
</View>
);
}}
/>
</View>
letter is my data, but you can ignore it. I just keep it here to explain why I need the pass
Why use useEffect to setState?
just set the state :
if (pass >= letter.description?.length) { setPass((prev) => 0); }
You can use UseEffect to render your component when you want to render it.
More of this here : https://reactjs.org/docs/hooks-effect.html

React Native rerendering UI after using array.map

I am attempting to have an icon switch its visual when clicked (like a checkbox). Normally in react native I would do something like this:
const [checkbox, setCheckbox] = React.useState(false);
...
<TouchableHighlight underlayColor="transparent" onPress={() => {setCheckbox(!setCheckbox)}}>
{added ? <MaterialIcons name="playlist-add-check" size={40} />
: <MaterialIcons name="playlist-add" size={40} />}
</TouchableHighlight>
However I have made some changes, and now I can't seem to replicate this behavior. I am using AsyncStorage class to storage and get arrays of objects for display. For simplification, in the example below I removed the storage code, and the objects each have an 'id' and an 'added' attribute, which is essentially the boolean value of the checkbox.
I am now attempting to update the icon shown to the user whenever it is pressed. I know the function is being called, but it will not update the icon. I am using array.map to create the list of icons. I created a demo here, and the code is below: https://snack.expo.dev/#figbar/array-map-icon-update
const templateObject = {
id: 0,
added: false,
};
const templateObject2 = {
id: 1,
added: true,
};
export default function App() {
const [savedNumbers, setSavedNumbers] = React.useState([]);
React.useEffect(() => {
setSavedNumbers([templateObject,templateObject2]);
}, []);
const populateSavedNumbers = () =>
savedNumbers.map((num, index) => <View key={index}>{renderPanel(num.id,num.added)}</View>);
const updateNumber = (id) => {
let capturedIndex = -1;
for(var i = 0; i < savedNumbers.length; i += 1) {
if(savedNumbers[i].id === id) {
capturedIndex = i;
break;
}
}
let _tempArray = savedNumbers;
_tempArray[capturedIndex].added = !_tempArray[capturedIndex].added;
setSavedNumbers(_tempArray);
}
const renderPanel = (id:number, added:boolean) => {
return (
<View>
<TouchableHighlight underlayColor="transparent" onPress={() => {updateNumber(id);}}>
{added ? <MaterialIcons name="playlist-add-check" size={40} />
: <MaterialIcons name="playlist-add" size={40} />}
</TouchableHighlight>
</View>
);
}
return (
<View>
<View>buttons:</View>
<View>{populateSavedNumbers()}</View>
</View>
);
}
This is a common React pitfall where things don't re-render when it seems like they should. React does shallow comparisons between new and old states to decide whether or not to trigger a re-render. This means that, when declaring a variable to simply equal a state variable which is an object or an array, a re-render is not triggered since those two variables now reference the same underlying data structure.
In this case, you are setting _tempArray to reference the array savedNumbers rather than creating a new array. Therefore, React's shallow comparison comes back as "equal", and it doesn't believe that a re-render is necessary.
To fix this, change this line:
let _tempArray = savedNumbers;
to this:
let _tempArray = [...savedNumbers];

Scroll down to a specific View in React Native ScrollView

I want to be able to scroll a after pressing button so that it visible on screen. How can I tell a react-native ScrollView move to a certain?
Hello you can use the property scrollTo like below
import {useRef} from "react"
import {
ScrollView,
Button,
} from 'react-native';
const YouComp = () => {
const refScrollView = useRef(null);
const moveTo = () => {
refScrollView.current.scrollTo({x: value1, y: value2});
// or just refScrollView.current.scrollTo({x: value1}); if you want to scroll horizontally
// or just refScrollView.current.scrollTo({y: value2}); if you want to scroll vertically
}
return (<>
<Button onPress={moveTo} title="title" />
<ScrollView ref={refScrollView}>
...
</ScrollView>
</>);
}
You can set whether x or y value or both
Check the full doc here
First you need to create reference to element
this.scrollViewRefName = React.createRef()
Then pass it to ref attribute
<ScrollView ref={this.scrollViewRefName}>
Then you trigger the function from your button with scrollToTheEnd or wherever you want to scroll within the element
<View style={styles.ButtonContainer}>
<Button onPress={() => { this.scrollViewRef.current.scrollToTheEnd }} />
</View>
Note that you may need extra callback function in onPress depending on from which context you have the components
using ref and scrollTo is just bullshit and dose not always work.
Here is how i did it.
const [scrollYPosition, setScrollYPosition] = useState(0);
const [data, setData] = useState([]);
const goToItem = () => {
// lets go to item 200
// the 200 is the item position and the 150 is the item height.
setScrollYPosition(200 * 150);
}
<ScrollView contentOffset = {
{
y: scrollYPosition,
x: 0
}
}>
// papulate your data and lets say that each item has 150 in height
</ScrollView>

TextInput /Input element looses focus on key press

I'm currently developing an application using React Native.
This trial app has a component that has a TextInput and two buttons (ADD and DELETE).
When I press the ADD Button, a new component appears. If I press the DELETE Button that the same component disappears.
The screen is like the photo bellow:
I control the TextInput with the index which is the same number as the index of the component.
My question is: why can't I enter some text as usual in this code?
I have to focus the cursor every time I enter 1 word.
I lose a flashing vertical bar(I check in the photo below) in the input area every time I press a key.
How can I resolve this problem?
And, I want to control the inputted value from TextInput with array[] not object{} because in case of an array is easier to delete a component sliding index and value like an explanation below:
I have no idea to control index and value with an, object and it's complicated for my skill now, but if there are some nice ways to resolve using object, I hope to know it.
Here is the code:
import React, { useState } from "react";
import { View, Text, Button, TextInput, StyleSheet } from "react-native";
function Item({ number, handleInput, handleAdd, handleDelete, index }) {
return (
<View style={styles.list}>
<Text>{index}</Text>
<TextInput
style={{ borderWidth: 1 }}
value={number[index]}
onChange={(e) => {
handleInput(index, e.nativeEvent.text);
}}
></TextInput>
<Button
title="ADD"
onPress={() => {
handleAdd();
}}
/>
<Button
title="DELETE"
onPress={() => {
handleDelete(index);
}}
/>
</View>
);
}
export default function TestStateArray() {
const [count, setCount] = useState(1);
const [number, setNumber] = useState([]);
function handleAdd() {
setCount((v) => v + 1);
}
function handleDelete(index) {
setCount((v) => v - 1);
setNumber((v) => {
const ret = v.slice();
ret.splice(index, 1);
return ret;
});
}
function handleInput(index, text) {
setNumber((v) => {
const ret = v.slice();
ret[index] = text;
return ret;
});
}
return (
<View>
{Array.from({ length: count }, (_, i) => (
<Item
number={number}
handleInput={handleInput}
handleAdd={handleAdd}
handleDelete={handleDelete}
key={i + "-" + number}
index={i}
/>
))}
</View>
);
}
const styles = StyleSheet.create({
list: {
margin: 10,
padding: 10,
backgroundColor: "#ddd",
},
});
After I have some answer and some comment, I tried changing the code like bellow, but it still has the same problem...
// onChange={(e) => {
// handleInput(index, e.nativeEvent.text);
onChangeText={(text) => {
handleInput(index, text);
}}
node : 12.18.3
react native : 4.10.1
expo : 3.22.3
The above issue occurred because your handle change function is wrong.
Please change...
const [number, setNumber] = useState({}); // change array to Object in useState.
Replace your handler with below function:
function handleInput(index, text) {
setNumber({ ...number, index: text });
}
React Native has onChangeText event on TextInput component can you try that one?

Resources