React Native Delete Function not working as intended - javascript

The intended function is that it would just delete the one goal that is clicked but for some odd reason it decides onPress to delete all goals listed.
I am following this tutorial https://www.youtube.com/watch?v=qSRrxpdMpVc and im stuck around 2:44:45. If anyone else has done this tutorial and or can see my problem an explanation would be greatly appreciated. :)
Program
import React, { useState } from "react";
import {
StyleSheet,
Text,
View,
Button,
TextInput,
ScrollView,
FlatList
} from "react-native";
import GoalItem from "./components/GoalItem";
import GoalInput from "./components/GoalInput";
export default function App() {
const [courseGoals, setCourseGoals] = useState([]);
const addGoalHandler = goalTitle => {
setCourseGoals(currentGoals => [
...currentGoals,
{ key: Math.random().toString(), value: goalTitle }
]);
};
const removeGoalHander = goalId => {
setCourseGoals(currentGoals => {
return currentGoals.filter((goal) => goal.id !== goalId);
});
};
return (
<View style={styles.screen}>
<GoalInput onAddGoal={addGoalHandler} />
<FlatList
keyExtractor={(item, index) => item.id}
data={courseGoals}
renderItem={itemData => (
<GoalItem
id={itemData.item.id}
onDelete={removeGoalHander}
title={itemData.item.value}
/>
)}
></FlatList>
</View>
);
}
const styles = StyleSheet.create({
screen: {
padding: 80
}
});
Function
import React from "react";
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
const GoalItem = props => {
return (
<TouchableOpacity onPress={props.onDelete.bind(this, props.id)}>
<View style={styles.listItem}>
<Text>{props.title}</Text>
</View>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
listItem: {
padding: 10,
backgroundColor: "lightgrey",
borderColor: "grey",
borderRadius: 5,
borderWidth: 1,
marginVertical: 10
}
});
export default GoalItem;

When updating state, you have to pass new array so component can detect changes and update view
const removeGoalHander = goalId => {
setCourseGoals(currentGoals => {
const newGoals = currentGoals.filter((goal) => goal.id !== goalId);
return [...newGoals];
});
};

Related

react native hover effect module not working properly in dynamic

I trying to make a onPress hover effect to show text, when I click on image it will show me a text layer on my image, as its working fine for one image & text, but while making dynamic.. clicked on one image then that effect, affecting to all images with text.
simply I want to show on single click show one text related to img, on different images on click show different text related to that images.
how can fix it..
App.js
import Button from "../custom-components/Button";
import React, { useState, useEffect } from "react";
import {
Image,
ImageBackground,
StyleSheet,
TouchableOpacity,
Text,
Touchable,
View,
FlatList,
} from "react-native";
import { ScrollView } from "react-native-gesture-handler";
import { BottomTabBarHeightCallbackContext } from "#react-navigation/bottom-tabs";
import axios from "axios";
import { API_URL } from "#env";
import { useSelector } from "react-redux";
export default function App() {
const languageState = useSelector((state) => state.languageReducer);
const [xx43d, setxx43d] = React.useState(languageState.language);
const [showText, setShowText] = useState(false);
const [apiData, setApiData] = useState([]);
React.useEffect(() => {
axios
.get(`${API_URL}/xyxsnn?xx43d=${xx43d}`)
.then((response) => {
if (response.status === 200) {
setApiData(response.data.result);
}
})
.catch((errors) => {
console.log(errors);
});
}, []);
return (
<>
<ScrollView>
<FlatList
data={apiData}
numColumns={2}
keyExtractor={(item, index) => index}
renderItem={({ item }) => (
<>
<TouchableOpacity
activeOpacity={0.5}
onPress={() => setShowText(!showText)}
>
<ImageBackground
source={{ uri: "https://reactjs.org/logo-og.png" }}
resizeMode="cover"
style={styles.image}
>
{!!showText && <Text style={styles.text}>{item.name}</Text>}
</ImageBackground>
</TouchableOpacity>
</>
)}
/>
</ScrollView>
</>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
image: {
justifyContent: "center",
height: 250,
width: 170,
margin: 12,
borderRadius: 10,
},
text: {
color: "white",
fontSize: 20,
fontWeight: "bold",
textAlign: "center",
backgroundColor: "#000",
},
});
Make Item a seprate component which has its own showText state.
Like
import Button from "../custom-components/Button";
import React, { useState, useEffect } from "react";
import {
Image,
ImageBackground,
StyleSheet,
TouchableOpacity,
Text,
Touchable,
View,
FlatList,
} from "react-native";
import { ScrollView } from "react-native-gesture-handler";
import { BottomTabBarHeightCallbackContext } from "#react-navigation/bottom-tabs";
import axios from "axios";
import { API_URL } from "#env";
import { useSelector } from "react-redux";
export default function App() {
const languageState = useSelector((state) => state.languageReducer);
const [xx43d, setxx43d] = React.useState(languageState.language);
const [apiData, setApiData] = useState([]);
React.useEffect(() => {
axios
.get(`${API_URL}/xyxsnn?xx43d=${xx43d}`)
.then((response) => {
if (response.status === 200) {
setApiData(response.data.result);
}
})
.catch((errors) => {
console.log(errors);
});
}, []);
return (
<>
<ScrollView>
<FlatList
data={apiData}
numColumns={2}
renderItem={({ item }) => <Item data={item}/>}
keyExtractor={(item, index) => index}
/>
</ScrollView>
</>
);
}
const Item = ({data}) => {
const [showText, setShowText] = useState(false);
return(<TouchableOpacity
activeOpacity={0.5}
onPress={() => setShowText(!showText)}
>
<ImageBackground
source={{ uri: "https://reactjs.org/logo-og.png" }}
resizeMode="cover"
style={styles.image}
>
{showText && <Text style={styles.text}>{data.name}</Text>}
</ImageBackground>
</TouchableOpacity>)
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
image: {
justifyContent: "center",
height: 250,
width: 170,
margin: 12,
borderRadius: 10,
},
text: {
color: "white",
fontSize: 20,
fontWeight: "bold",
textAlign: "center",
backgroundColor: "#000",
},
});

How to use onChange in custom textinput react native

So im new at react native and i built an custom text input that gives suggestions by following a tutorial.But now i cant use onChange in that custom text input.I tried to create a state in App.js and change it in AutoCompleteText.js file but didnt worked.How can i get the value inmy custom text input ?
my App.js file :
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import AutoCompleteText from './AutoCompleText'
import './AutoCompleteText.css';
export default function App() {
return (
<View style={styles.container}>
<View style={styles.AutoComp}>
<AutoCompleteText items={['Ali','Veli','Ahmet']} />
</View>
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
AutoComp:{
width:600
}
});
my AutoCompleteText.js file
import React from 'react'
import './AutoCompleteText.css';
export default class AutoCompleteText extends React.Component{
constructor(props){
super(props);
this.state={
suggestions:[],
text:'',
};
}
onTextChanged = (e) =>{
const {items} = this.props;
const value = e.target.value;
let suggestions = [];
if (value.length > 0){
const regex = new RegExp(`^${value}`,'i');
suggestions = items.sort().filter(v => regex.test(v));
}
this.setState(() => ({ suggestions , text: value }));
}
suggestionSelected(value){
this.setState(() =>({
text: value,
suggestions:[],
}) )
}
renderSuggestions(){
const {suggestions} = this.state;
if (suggestions.length === 0){
return null;
}
return(
<ul>
{suggestions.map((item) => <li onClick={() => this.suggestionSelected(item)}>{item}</li>)}
</ul>
);
}
render(){
const { text } = this.state;
return(
<div className="AutoCompleteText">
<input value={text} onChange={this.onTextChanged} type ='text'/>
{this.renderSuggestions()}
</div>
)
}
}
hi Ülkü Tuncer Küçüktaş,
You are writing the wrong syntax. You are mixing the syntax of react native with react. In react native for textinput, you should use the TextInput Component(Built in component from docs).
The syntax of react native TextInput look like below
import React from 'react';
import { TextInput } from 'react-native';
const UselessTextInput = () => {
const [value, onChangeText] = React.useState('Useless Placeholder');
return (
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={text => onChangeText(text)}
value={value}
/>
);
}
export default UselessTextInput;

How would I call a function from another component in react native?

How would I call a function from another component?
App.js
<Button onPress={movePopUpCard()}></Button>
<PopUpCard/>
PopUpCard.js
const movePopUpCard = () => {
//Code to update popUpCardX
}
<Animated.View style={[
{transform: [{scale: scale}, {translateX: popUpCardX}]},
{position: 'absolute'}
]}>
</Animated.View>
By using ref and forwardRef you can achieve this.
snack ex: https://snack.expo.io/#udayhunter/frisky-pretzels
App.js
import React,{useRef,useEffect} from 'react';
import { Text, View, StyleSheet } from 'react-native';
import PopUp from './PopUpCard'
const App =(props)=> {
const childRef = useRef(null)
return (
<View style={styles.container}>
<PopUp ref={childRef}/>
<Text
onPress = {()=>{
childRef.current.updateXvalue(10)
}}
>Press me</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ecf0f1',
padding: 8,
},
});
export default App
popUp.js
import React,{useState,useImperativeHandle,useEffect,forwardRef} from 'react'
import {View,Text,StyleSheet,Animated} from 'react-native'
const PopUp = (props,ref) => {
const [xValue, setXvalue] = useState(10)
const updateXvalue = (x)=>{
setXvalue(prev=> prev+x)
}
useImperativeHandle(ref, () => ({
updateXvalue
}));
return(
<Animated.View style = {[styles.mainView,{transform:[{translateX:xValue}]}]}>
</Animated.View>
)
}
const styles = StyleSheet.create({
mainView : {
height:100,
width:100,
backgroundColor:'red'
}
})
export default forwardRef(PopUp)
If you are using a functional component you have to use forwardRef to add reference to the component and useImperativeHandle to implement the method which you want to access through reference here is an example. Here I am calling bounce method which increases the scale of the component:
Snack: https://snack.expo.io/#ashwith00/adequate-salsa
Code:
App.js
export default function App() {
const boxRef = React.useRef();
return (
<View style={styles.container}>
<AnimatedBox ref={boxRef} />
<Button title="Bounce" onPress={() => boxRef.current.bounce()} />
</View>
);
}
AnimatedBox.js
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
import { View, Animated } from 'react-native';
export default forwardRef((props, ref) => {
const scale = useRef(new Animated.Value(1)).current;
useImperativeHandle(
ref,
() => ({
bounce: () => {
Animated.timing(scale, {
toValue: 1.5,
duration: 300,
}).start(({ finished }) => {
if (finished) {
Animated.timing(scale, {
toValue: 1,
duration: 300,
}).start();
}
});
},
}),
[]
);
return (
<Animated.View
style={{
width: 100,
height: 100,
backgroundColor: 'red',
transform: [{scale}]
}}
/>
);
});
move the function movePopUpCard to app.js
and pass in app.js pass it in
<PopUpCard movePopUpCard={(params) => movePopUpCard(params)}/>
you can call the function from PopUpCard component by this.props.movePopUpCard(params)
you can also pass params if requrired

Too many re-renders on search API

I am new to this so I hope this is the right place to get help!
As titled, executing this code is giving me the "Too many re-renders" error on React.
I have tried going through all lines and checking my hooks repeatedly, but nothing seems to work.
I am guessing this is happening due to useEffect, so pasting the code for the relevant components below:
UseResults:
import { useEffect, useState } from 'react';
import yelp from '../api/yelp';
export default () => {
const [results, setResults] = useState([]);
const [errorMessage, setErrorMessage] = useState('');
const searchApi = async () => {
try {
const response = await yelp.get('/search', {
params: {
limit: 50,
term,
location: 'san francisco'
}
});
setResults(response.data.businesses);
} catch (err) {
setErrorMessage('Something went wrong')
}
};
useEffect(() => {
searchApi('pasta');
}, []);
return [searchApi, results, errorMessage];
}
SearchScreen:
import React, { useState } from 'react';
import { Text, StyleSheet } from 'react-native';
import { ScrollView } from 'react-native-gesture-handler';
import ResultsList from '../components/ResultsList';
import SearchBar from '../components/SearchBar';
import useResults from '../hooks/useResults';
const SearchScreen = (navigation) => {
const [term, setTerm] = useState('');
const [searchApi, results, errorMessage] = useResults();
const filterResultsByPrice = (price) => {
return results.filter(result => {
return result.price === price;
});
};
return <>
<SearchBar
term={term}
onTermChange={setTerm}
onTermSubmit={searchApi()}
/>
{errorMessage ? <Text>{errorMessage}</Text> : null}
<Text>We have found {results.length} results</Text>
<ScrollView>
<ResultsList
results={filterResultsByPrice('$')}
title="Cost Effective"
navigation={navigation}
/>
<ResultsList
results={filterResultsByPrice('$$')}
title="Bit Pricier"
navigation={navigation}
/>
<ResultsList
results={filterResultsByPrice('$$$')}
title="Big Spender"
navigation={navigation}
/>
</ScrollView>
</>
};
const styles = StyleSheet.create({});
export default SearchScreen;
ResultsList:
import React from 'react';
import { View, Text, StyleSheet, FlatList } from 'react-native';
import { TouchableOpacity } from 'react-native-gesture-handler';
import ResultsDetail from './ResultsDetail';
const ResultsList = ({ title, results, navigation }) => {
return (
<View style={styles.container} >
<Text style={styles.title}>{title}</Text>
<FlatList
horizontal
showsHorizontalScrollIndicator={false}
data={results}
keyExtractor={result => result.id}
renderItem={({ item }) => {
return (
<TouchableOpacity onPress={() => navigation.navigate('ResultsShow')}>
<ResultsDetail result={item} />
</TouchableOpacity>
)
}}
/>
</View>
);
};
const styles = StyleSheet.create({
title: {
fontSize: 18,
fontWeight: 'bold',
marginLeft: 15,
marginBottom: 5
},
container: {
marginBottom: 10
}
});
export default ResultsList;
TIA!

React Native changing background color according to data that comes through http request from server

I am new at React Native.
I'm working on a code that will change the background color of a box (card) according to the data that I will get from API. I want to check first if the title is like 'Taylor' make background red , if it is 'Fearless' make it green and so on.
Here is the API that I got information from :
http://rallycoding.herokuapp.com/api/music_albums
This is the code divided into several files.
First of them index.js
// Import a library to help to create a component
import React from 'react';
import { Text, AppRegistry, View } from 'react-native';
import Header from './src/components/header.js';
import AlbumList from './src/components/AlbumList.js'
// create a component
const App = () => (
<View>
<Header headerText={'Smart Parking'}/>
<AlbumList />
</View>
);
//render it to the device
AppRegistry.registerComponent('albums2', () => App);
second is AlbumList.js
import React, { Component } from 'react';
import { View } from 'react-native';
import axios from 'axios';
import AlbumDetail from './AlbumDetail.js'
class AlbumList extends Component {
state = { albums: [] };
componentWillMount() {
axios.get('https://rallycoding.herokuapp.com/api/music_albums')
.then(response => this.setState({ albums: response.data }) );
}
renderAlbums() {
return this.state.albums.map(album =>
<AlbumDetail key={album.title} album={album} />
);
}
render() {
return(
<View>
{this.renderAlbums()}
</View>
);
}
}
export default AlbumList;
3rd is AlbumDetail.js
import React from 'react';
import {Text, View} from 'react-native';
import Card from './Card.js'
const AlbumDetail = (props) => {
return(
<Card>
<Text> {props.album.title} </Text>
</Card>
);
};
export default AlbumDetail;
4th is card which I need to change background of it
import React from 'react';
import { View } from 'react-native';
const Card = (props) => {
return (
<View style={styles.containerStyle}>
{props.children}
</View>
);
};
const styles = {
containerStyle:{
borderWidth: 1,
borderRadius: 2,
backgroundColor: '#ddd',
borderBottomWidth: 0,
shadowColor: '#000',
shadowOffset: {width: 0, height:2 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 1,
marginLeft: 5,
marginRight: 5,
marginTop: 10
}
};
export default Card;
last one is header
// Import libraries for making components
import React from 'react';
import { Text, View } from 'react-native';
// make a components
const Header = (props) => {
const { textStyle, viewStyle } = styles;
return(
<View style={viewStyle}>
<Text style={textStyle}>{props.headerText}</Text>
</View>
)
};
const styles ={
viewStyle:{
backgroundColor:'orange',
justifyContent: 'center',
alignItems: 'center',
height: 60,
},
textStyle: {
fontSize: 20
}
};
// make the component to the other part of the app
export default Header;
Basically you need to pass the title of the album as prop to the Card from the AlbumDetails component and then on each Card calculate the color to use and pass it in the style like this:
// AlbumDetails.js component
import React from 'react';
import {Text, View} from 'react-native';
import Card from './Card.js'
const AlbumDetail = (props) => {
return(
<Card title={props.album.title}>
<Text> {props.album.title} </Text>
</Card>
);
};
export default AlbumDetail;
// Card.js component
import React from "react";
import { View } from "react-native";
function calculateColor(title) {
let bgColor;
switch (title) {
case "Taylor":
bgColor = "red";
break;
case "Fearless":
bgColor = "green";
break;
default:
bgColor = "orange";
break;
}
return bgColor;
}
const Card = props => {
const { title } = props;
const backgroundColor = calculateColor(title);
return (
<View style={[styles.containerStyle, { backgroundColor: backgroundColor }]}>
{props.children}
</View>
);
};
const styles = {
containerStyle: {
borderWidth: 1,
borderRadius: 2,
backgroundColor: "#ddd",
borderBottomWidth: 0,
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 1,
marginLeft: 5,
marginRight: 5,
marginTop: 10
}
};
export default Card;
Something like this should work:
Change the AlbumDetail to conditionally render the Card.
const AlbumDetail = props => {
if (props.album.title === 'Taylor') {
return (
<Card style={{ backgroundColor: 'red' }}>
<Text>{props.album.title}</Text>
</Card>
);
} else {
return (
<Card style={{ backgroundColor: 'green' }}>
<Text>{props.album.title}</Text>
</Card>
);
}
};
Override the default style of the card using the passed style prop.
const Card = props => {
return (
<View style={[styles.containerStyle, props.style]}>{props.children}</View>
);
};
accepted answer is great just its a bad habit to do things in render.
i also not sure since every title has a color why wouldnt the server send this in the object props in first place ? :)
class AlbumList extends Component {
state = { albums: [] };
componentDidMount() {
axios.get('https://rallycoding.herokuapp.com/api/music_albums')
.then(response=> Array.isArray(response.data) ? response.data : []) // alittle validation would not hurt :) !
.then(data => this.setState({ albums: data }) );
}
selectHeaderColorForAlbum( album ){
let headerColor = 'red';
// switch(album.title){ ..you logic }
return headerColor;
}
renderAlbums() {
return this.state.albums.map(album =>
<AlbumDetail key={album.title} album={album} color={this.selectHeaderColorForAlbum(album)} />
);
}
render() {
return(
<View>
{this.renderAlbums()}
</View>
);
}
}
const Card = (props) => {
return (
<View style={[styles.containerStyle,{color:props.headerColor}]}>
{props.children}
</View>
);
};
this is easier your logic will render only once.
also notice that react >16.0 depricated componentWillMount, so use DidMount

Categories

Resources