How can I delete components one by one? [duplicate] - javascript

This question already has answers here:
How to delete an item from state array?
(18 answers)
Closed 2 years ago.
I'm currently developing an application using React Native.
This trial app has 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.
My question is: why can't I delete components one by one in this code?
even though I select an index of components I want to delete, all components disappear when I press a DELETE button...
How can I resolve this problem?
Here is the code (Screen):
import React, { useState } from "react";
import { ScrollView } from "react-native";
import Items from "../components/Items";
export default function Screen() {
const items = {
lists: [""],
};
const [stateItems, setStateItems] = useState(items);
return (
<ScrollView>
<Items stateItems={stateItems} setStateItems={setStateItems} />
</ScrollView>
);
}
Here is the code (Components):
import React from "react";
import { StyleSheet, View, Text, TouchableOpacity } from "react-native";
function Item({ index, handleAdd, handleDelete }) {
return (
<View style={styles.list}>
<TouchableOpacity
onPress={() => {
handleDelete(index);
}}
style={styles.buttonDelate}
>
<Text>DELETE</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
handleAdd();
}}
style={[styles.buttonDelate, styles.buttonAdd]}
>
<Text>ADD</Text>
</TouchableOpacity>
</View>
);
}
export default function Items(props) {
const stateItems = props.stateItems;
const setStateItems = props.setStateItems;
const handleAdd = () => {
setStateItems({
...stateItems,
lists: [...stateItems.lists, ""],
});
};
const handleDelete = (index) => {
const ret = stateItems.lists;
setStateItems({ ...ret, lists: [...ret].splice(index, 1) });
};
return (
<View>
{stateItems.lists.map((value, i) => (
<Item
key={value + i.toString()}
index={i}
stateItems={stateItems}
setStateItems={setStateItems}
handleAdd={handleAdd}
handleDelete={handleDelete}
/>
))}
</View>
);
}
const styles = StyleSheet.create({
list: {
width: "100%",
backgroundColor: "#ddd",
margin: 5,
padding: 10,
},
buttonDelate: {
backgroundColor: "tomato",
width: 80,
height: 40,
borderRadius: 5,
margin: 3,
justifyContent: "center",
alignItems: "center",
alignSelf: "center",
},
buttonAdd: {
backgroundColor: "orange",
},
});
node : 12.18.3
react native : 4.10.1
expo : 3.22.3

Splice returns the deleted items, not the original array. So when you do:
setStateItems({ ...ret, lists: [...ret].splice(index, 1) });
you're setting lists to the items deleted from [...ret], not the remaining ones.
We might also want to address why you're spreading the list into state and also setting lists to the deleted items from a copy of the original ret array.
const handleDelete = (index) => {
const ret = stateItems.lists;
setStateItems({ ...ret, lists: [...ret].splice(index, 1) });
};
The first part of this is stomping your other state with the lists array, which I highly doubt is what you want. Maybe you intended to preserve other state properties? Something more like:
const handleDelete = (index) => {
const { lists: old, ...others } = stateItems;
const lists = [...old];
lists.splice(index, 1);
setStateItems({ ...others, lists });
};

Related

Render results on submit react native

New to react native. I want to render what is currently being logged in the console into a separate jsx Text elements, but I've been having trouble.
const handleSubmit = async () => {
const response = await fetch(
`http://127.0.0.1:5000/GetRecipes/${searchText}`,
{
method: "GET",
}
);
const result = await response.json();
const recipes = result["recs"];
for (var meal in recipes) {
if (recipes.hasOwnProperty(meal)) {
console.log(recipes[meal].meal); // want to display this
setSubmitPressed(submitPressed);
}
}
// want to render the results in text elements
const Test = () => {
return <Text>hi</Text>;
};
const DisplayRecipes = ({ recipes }) => {
return (
<View>
<Text>Meals</Text>
{recipes.map((ing) => (
<View key={ing[meal].meal}>
<Text>{ing[meal].meal}</Text>
</View>
))}
</View>
);
};
};
The results should only be displayed after a user submits input from a text input field and the results are fetched.
HandleSubmit is called in the onPress prop of a button.
The reference data looks like this
{0, {“meal”: “Chicken Wings”}}, {1, {“meal”: “Hamburger”}}
Please find below code:
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
// You can import from local files
import AssetExample from './components/AssetExample';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
export default function App() {
const recipes = [
{
"id": 0,
"meal": "ChickenWings"
},
{
"id": 1,
"meal": "Hamburger"
}
]
return (
<View style={styles.container}>
<Text>Meals</Text>
{recipes.map((ing) => (
<View key={ing.id}>
<Text>{ing.meal}</Text>
</View>
))}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
});
You can set your data as per requirement.

Storing dynamically rendered page elements in state, and keeping variables functional?

I might have asked this question in a confusing way, so I apologize. But I have a map function that renders a lot of text elements dynamically and also styles them based on state. However, the calculations used to create the elements in the first place seems really expensive performance-wise. So I would like to render them once, and then store the created elements in state, and yet still have the styling rerender when it needs to.
I tried storing these mapped elements in an array, but the styling variables inside of each component are set to a single value when the component is stored. So rerendering the page doesn't change the styling of these components even if the initial variables used to set their styles in state have changed.
import React, {useState} from 'react';
import { Text, View, StyleSheet } from 'react-native';
export default function App() {
let [redText, setRedText] = useState(['This', 'That'])
let [blueText, setBlueText] = useState(['The', 'Other'])
let str = 'This That And The Other'
let arr = str.split(" ")
let componentsArr = null
function firstRender() {
componentsArr = []
componentsArr.push(arr.map((el) => {
return (
<View style={styles.container}>
<Text style={redText.includes(el)
? styles.redText
: blueText.includes(el)
? styles.blueText
: styles.blackText}>
{el}
</Text>
</View>
)
}))
return componentsArr
}
return (
<View style={styles.container}>
{componentsArr ? componentsArr : firstRender()}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
blackText: {
color: "black"
},
redText: {
color: "red"
},
blueText: {
color: "blue"
}
});
Let's say I have some code like this that adds an onPress event to each element that will automatically change it to red. How can I do that without mapping through and creating the View and Text components from scratch?
When it is initially pushed into the array, all of the styling variables are set in stone. Is there some way to preserve the ternary operations?
i'm not sure i understand well what you wanted to do but as i understood, every word in the text must manage it's own toggle color ? So here how i would go.
export const App = () => {
const [texts, setTexts] = useState(['This', 'That', 'And', 'The', 'Other']);
const renderTexts = () => {
return texts.map(text => (
<CustomTextColorToggle el={text} key={text} />
));
};
return (
<View style={styles.container}>
{renderTexts()}
</View>
);
}
// Here defaultColor is optional if you want to add some
// more logic
const CustomTextColorToggle = ({ defaultColor, el }) => {
const [color, setColor] = useState(defaultColor);
const styles = color === "red"
? styles.redText
: color === "blue"
? styles.blueText
: styles.blackText;
return (
<View style={styles.container}>
<Text style={styles}>
{el}
</Text>
</View>
);
};
Inside CustomTextColorToggle you can wrap the View with a Pressable to change the color using setColor
This sounds like a good use case for memoization. If you want to prevent rendering of the list and its elements, unless styles change, you need to apply this in two places, the <View/> wrapper containing el and the entire list itself. Below is how I would apply it.
import React, { useState, memo, useEffect } from "react";
import { Text, View, StyleSheet } from "react-native";
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
blackText: {
color: "black",
},
redText: {
color: "red",
},
blueText: {
color: "blue",
},
});
const stylesAreEqual = (prevProps, nextProps) => {
//we only compare styles since we assume el prop wont change
return (
prevProps.redText === nextProps.redText &&
prevProps.blueText === nextProps.blueText
);
};
//this component only re-renders if redText or blueText change.
//if el changes, it doesnt re-render. If you want this behavior to change,
//remove the stylesAreEqual function from the memo callback
const ViewItem = ({ el, redText, blueText }) => {
//responds to any styles passed into the component
const propStyle = redText.includes(el)
? styles.redText
: blueText.includes(el)
? styles.blueText
: styles.blackText;
//if you want to control the styles indiviually for each view item without needing
// to change redText, or blue Text props. It can managed locally here
const [style, setStyle] = useState(propStyle);
const onPressEvent = () => {
//write any change to style on a press
setStyle({ color: "black" });
};
useEffect(() => {
//if you want to respond to changed passed from a higher up component.
// though consider using useContext for this if your handling anything other
// than primary types (i.e strings, boolean, etc.)
setStyle(propStyle);
}, [propStyle]);
return (
<View style={styles.container}>
<Text onPress={onPressEvent} style={style}>
{el}
</Text>
</View>
);
};
const MemoViewItem = ({ el, redText, blueText }) =>
memo(
<ViewItem el={el} redText={redText} blueText={blueText} />,
stylesAreEqual
);
const MemoList = ({ arr, redText, blueText }) =>
//this means that unless the props passed into this component change,
// it will not re-render, even if a component above it does for any case.
memo(
<>
{arr.map((el) => {
return <MemoViewItem el={el} redText={redText} blueText={blueText} />;
})}
</>
);
export default function App() {
let [redText, setRedText] = useState(["This", "That"]);
let [blueText, setBlueText] = useState(["The", "Other"]);
let str = "This That And The Other";
let arr = str.split(" ");
let componentsArr = null;
function firstRender() {
componentsArr = [];
componentsArr.push(
<MemoList arr={arr} blueText={blueText} redText={redText} />
);
return componentsArr;
}
return (
<View style={styles.container}>
{componentsArr ? componentsArr : firstRender()}
</View>
);
}
Since I'm unsure if you want to change styles from onpress events, or from a general state coming from a higher component, I've included both in this example. In addition, depending on your use case, you can modify this above, and test where you need memoization or not since adding it does add extra overhead if its not necessary.
As a note though, the only way to prevent the list from re-rendering at all (only once on mount), is to manage the styles in a local component
In this case, removing redText and blueText from the App component, and removing the props on every component down the tree. From there, you can manage the styles inside the ViewItem component. Should this be the case, you can also remove the memo function. Below is an example.
import React, { useState, memo } from "react";
import { Text, View, StyleSheet } from "react-native";
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
blackText: {
color: "black",
},
redText: {
color: "red",
},
blueText: {
color: "blue",
},
});
const ViewItem = ({ el }) => {
//if you want to control the styles indiviually for each view item without needing
// to change redText, or blue Text props. It can managed locally here
const [redText, setRedText] = useState(["This", "That"]);
const [blueText, setBlueText] = useState(["The", "Other"]);
const style = redText.includes(el)
? styles.redText
: blueText.includes(el)
? styles.blueText
: styles.blackText;
//const [styles, setStyles] = useState({});
const onPressEvent = () => {
//write any change to style on a press
setRedText("Cool");
};
return (
<View style={styles.container}>
<Text onPress={onPressEvent} style={style}>
{el}
</Text>
</View>
);
};
//prevent list from re-rendering unless app prop changes
const MemoList = ({ arr }) => memo(
<>
{arr.map((el) => (
<ViewItem el={el} />
))}
</>
);
export default function App() {
let str = "This That And The Other";
let arr = str.split(" ");
let componentsArr = null;
function firstRender() {
componentsArr = [];
componentsArr.push(<MemoList arr={arr} />);
return componentsArr;
}
return (
<View style={styles.container}>
{componentsArr ? componentsArr : firstRender()}
</View>
);
}

RefreshControll data duplicate everytime do pull to refresh on ScrollView in React native

Description
I implement a pull request using RequestController in React Native, every time I did pull to refresh the same data keep adding on in Flat list over and over. I implemented pull request not inside the Flat list but on the ScrollView which wrapped the FlatList.
Actions
import React, { Component } from 'react';
import { View, StyleSheet, Text, Button, Modal, Dimensions, ScrollView, TextInput, TouchableOpacity, StatusBar, Image, Platform, TouchableNativeFeedback,FlatList, ImageBackground, RefreshControl } from 'react-native';
import axios from 'axios';
class HomeScreen extends Component{
state = {
refreshing: false,
}
componentDidMount(){
this.searchApi();
}
searchApi = async() => {
const response = await axios.get('http://73udkYid.ngrok.io/api/v1/products',{
headers: {
"x-auth-token":"eyJfaWQiOiI1ZdfjzZmM4YjIwYjBjZDQyMmJkNzUiLCJpYXQiOjE2MD"
}
}
);
this.setState({results: [...this.state.results, response.data]});
}
_onRefresh = () => {
this.setState({refreshing: true});
this.searchApi().then(() => {
this.setState({refreshing: false});
});
}
render(){
let finalGames = [];
this.state.results.forEach(obj => {
Object.keys(obj).forEach(key => {
finalGames.push(obj[key]);
});
});
return (
<ScrollView style={{flex: 1,backgroundColor: 'white',}}
refreshControl = {
<RefreshControl
refreshing = { this.state.refreshing }
onRefresh={this._onRefresh}
/>
}
>
<FlatList
data = { finalGames }
keyExtractor = {(item, index) => index.toString()}
renderItem = { ({item: itemData}) => {
if(itemData.empty == true){
return <View style = {[styles.item,styles.itemInvisible]}/>
}
return (
<View style = {{ flex: 1, margin: 4}}>
<View style = {styles.item}>
<TouchableOpacity
onPress = {() => {
this.setState({ viewController: this.state.viewController++ })
this.props.navigation.navigate(
"ProductDetail", {
itemDataDetail: itemData,
businessname:this.props.navigation.state.params.businessname,
viewController: this.state.viewController,
})
}}>
<View>
<ImageBackground
source={{ uri: itemData.photo }}
style={{ width:'100%',aspectRatio: 1, borderRadius: 15, borderWidth:1, borderColor:"#FAFAFA", overflow: 'hidden'}}>
</ImageBackground>
<View style = {{ margin: 5}}>
<Text style = {{color: '#2E2E2E', fontWeight:"bold"}} numberOfLines={1}>{itemData.item}</Text>
<Text style = {{color: '#2E2E2E', fontSize: 12, fontWeight:"normal", alignSelf: 'flex-start'}} numberOfLines={1}>Available now | Sold out</Text>
<Text style = {{color: 'white', fontSize: 18, fontWeight:"bold", backgroundColor:"#DE1F38", alignSelf: 'flex-start', paddingHorizontal: 10,paddingVertical:2,borderRadius: 8,overflow: 'hidden', marginTop: 5}} numberOfLines={1}>${itemData.price}</Text>
</View>
</View>
</TouchableOpacity>
</View>
</View>
</ScrollView>
);
}}/>
}
Output
Data duplicated every time new pull refresh triggered
I assume your api-call returns the whole list of products
This line concat api-response-data to the the list of products you already have in your component-state
this.setState({results: [...this.state.results, response.data]});
Try this instead...
this.setState({ results: response.data });
You should replace your data instead of concatenating. Use:
this.setState({ results: response.data });
Also, you should use FlatList 'onRefresh' prop to implement refresh functionality instead of using an extra ScrollView on the parent.
Oh I found a way. I just need to do this.
this.setState({results: [response.data]});
I was facing the same problem as you,
When I refreshed, the data was (data)+[(data)+(new_data)].
What happens here is that data is added to the array of this variable: results.
To prevent this you must first clear this variable: results.
So your code will look like this.
state = {
refreshing: false,
results : [],
}
when API runs, this array will filled results[{some_data},{some_data},{some_data},..],
While you refresh->
1st: The results will Empty,
2nd: reassign that array with newly added data from API.
_onRefresh = () => {
this.setState({results: []});
this.setState({refreshing: true});
this.searchApi().then(() => {
this.setState({refreshing: false});
});
}

How can I conserve data when I change screen in React-Native? [duplicate]

This question already has answers here:
How to use global variables in React Native?
(7 answers)
Closed 2 years ago.
In my code, I have a page that shows a list of elements retrieved from a database. However, I also added a page that allows the user to select several filters.
I have two problems:
I must save the data after I get away from the page (it musts stay there)
I must transfer the data back to the list of elements.
I thought that by giving the filter screen a var from the previous screen, it would save the data in there, but it doesn't seem to work.
Do you have any indication about what I should do ?
import React from 'react'
import { SafeAreaView, View, Text } from 'react-native'
import {generalStyles} from '#gym-app/styles/general'
import { useTranslation } from 'react-i18next'
import { TouchableOpacity } from 'react-native-gesture-handler';
export default function ExerciseSelectorScreen({navigation}) {
const {t} = useTranslation();
var filterProps = {};
return (
<SafeAreaView style={generalStyles.contentContainer}>
<View style={[generalStyles.contentContainer]}>
<Text>{JSON.stringify(filterProps)}</Text>
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<Text style={generalStyles.title}>{ t('exercise.exercises') }</Text>
<TouchableOpacity onPress={() => {navigation.navigate('Exercises Filter', {filterProps: filterProps})}} style={{padding: 10, backgroundColor: '#ffb623'}}>
<Text style={{fontSize: 20, fontWeight: 'bold'}}>{t('general.filters').toUpperCase()}</Text>
</TouchableOpacity>
</View>
</View>
</SafeAreaView>
)
}
import React, { useState, useEffect } from 'react'
import { View, SafeAreaView, Text, ScrollView, StyleSheet } from 'react-native'
import {generalStyles} from '#gym-app/styles/general'
import { useTranslation } from 'react-i18next'
import persistent from '#gym-app/database/persistent'
import tdb from '#gym-app/translation/object'
import CheckboxGroup from '../../components/workout/exercises/filters/CheckboxGroup'
import { useRoute } from '#react-navigation/native';
export default function ExercisesFilterScreen() {
const {t} = useTranslation();
const route = useRoute();
var filterProps = route.params.filterProps;
const [equipments, setEquipments] = useState({});
const [selectedEquipments, setSelectedEquipments] = useState({});
const [order, setOrder] = useState('');
const [machine, setMachine] = useState('yes');
useEffect(()=> {
if (Object.values(equipments).length == 0) {
persistent.transaction(tx => {
tx.executeSql(
"SELECT * FROM equipment",
[],
(t, s) => {
var transEquip = {};
Object.values(s.rows._array).map((equipment) => {
transEquip[equipment.id] = tdb(equipment, 'name')
})
setEquipments(transEquip);
},
(t, e) => {
console.log(e);
}
);
});
}
},[equipments])
useEffect(() => {
filterProps.selectedEquipments = selectedEquipments;
filterProps.order = order;
filterProps.machine = machine;
})
return (
<SafeAreaView style={{flex: 1}}>
<ScrollView style={[generalStyles.contentContainer, {flex: 1, backgroundColor: '#ffb623'}]}>
<Text style={{fontSize: 30, fontWeight: 'bold', color: '#ffffff', textAlign: 'center'}}>{t('general.filters').toUpperCase()}</Text>
<View style={{marginTop: 10}}>
<CheckboxGroup
title={t('exercise.availableEquipment')}
selected={selectedEquipments}
select={setSelectedEquipments}
multi={true}
options={
equipments
}
/>
<CheckboxGroup
title={t('exercise.order')}
selected={order}
select={setOrder}
multi={false}
options={
{
"asc": t('exercise.easyToHard'),
"desc": t('exercise.hardToEasy'),
}
}
undefined={'allow'}
/>
<CheckboxGroup
title={t('exercise.machines')}
selected={machine}
select={setMachine}
multi={false}
options={
{
"yes": t('exercise.machine.yes'),
"no": t('exercise.machine.no'),
"only": t('exercise.machine.only')
}
}
/>
</View>
</ScrollView>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
optionTitle: {
marginBottom: 6,
fontSize: 24,
fontWeight: 'bold',
color: '#ffffff',
textAlign: 'left'
}
})
Best way to use "global variable". By using this, you can access your data on multiple screen and re-render in your flat-list. (Redux is also the solution but initial is difficult to understand). you can initial you global variable in constructor and can update any where in the screen. here is link you can make understanding of global variable in react-native.
https://aboutreact.com/react-native-global-scope-variables/
Either you can use redux to store the state irrespective of if you navigate to naother screen or not. It will store the state globally and whenever you need you can use its mapStateToProps method to retrieve the state value. Check this doc : react-redux . You can also save the store data even when the app closes by using redux-persist, I personally prefer this as it lessens the hassle of using Asyncstorage.
Even you can use just Asyncstorage to save data.
Hope it helps .feel free for doubts

React Native Flatlist returns the wrong number of empty rows

When I run the code below, it displays 3 empty rows. It should be showing two rows each with a color and enddate and I want to use the 'Parent' as the unique key. The 'Parent' is the unique key created by Firebase when color and enddate were pushed to Firebase with '.push'.
I've tried all sorts of things to get it to display. I did get content to display when I made the 'renderItems' return 'this.state.list', but that returned 3 lines all with the same data, which is the content of the last array on the console log.
I would really appreciate some help to get this working.
Here is the code, a copy of Firebase database and the console.log. Please note that the Firebase 'goal' has been changed to 'color'.
import React, { Component } from 'react';
import { Text, FlatList, View, Image } from 'react-native';
import firebase from 'firebase';
import { Button, Card, CardSection } from '../common';
import styles from '../Styles';
class List extends Component {
static navigationOptions = {
title: 'List',
}
constructor(props) {
super(props);
this.state = {
list: [],
};
}
componentDidMount() {
const { currentUser } = firebase.auth();
const Parent = firebase.database().ref(`/users/${currentUser.uid}/Profile`);
Parent.on(('child_added'), snapshot => {
this.setState({ list: [snapshot.key, snapshot.val().color, snapshot.val().enddate] });
console.log(this.state.list);
});
}
keyExtractor = (item, index) => index;
render() {
return (
<Card>
<View style={{ flex: 1 }}>
<FlatList
data={this.state.list}
keyExtractor={this.keyExtractor}
extraData={this.state}
renderItem={({ item }) => (
<Text style={styles.listStyle}>
{ item.color }
{ item.enddate }
</Text>
)}
/>
</View>
<CardSection>
<Button
style={{
flex: 1,
flexDirection: 'row'
}}
onPress={() => this.props.navigation.navigate('NextPage', { name: 'user' })}
title="Go to next page"
>
Go to next page
</Button>
</CardSection>
</Card>
);
}
}
export { List };
This is the correct way to store the list
componentDidMount() {
const { currentUser } = firebase.auth();
const Parent = firebase.database().ref(`/users/${currentUser.uid}/Profile`);
Parent.on(('child_added'), snapshot => {
const newChild = {
key: snapshot.key,
color: snapshot.val().color,
enddate: snapshot.val().enddate
}
this.setState((prevState) => ({ list: [...prevState.list, newChild] }));
console.log(this.state.list);
});
}
and your keyExtractor
keyExtractor = (item, index) => item.key;

Categories

Resources