Related
I got array:
const DATA_Section = [
{
id: 0,
text: "Some text1",
},
{
id: 1,
text: "Some text2",
},
{
id: 2,
text: "Some text 3",
},
];
and I want to use text from this array to create dynamic component. Idea is: I can see text[0], I click some button then I can see text[1], something like loop. How can to do it?
You can do this way maybe.
const DATA_Section = [
{
id: 0,
text: "Some text1",
},
{
id: 1,
text: "Some text2",
},
{
id: 2,
text: "Some text 3",
},
];
const Component: FC = () => {
const [initialRenderState, setInitialRenderState] =
useState(DATA_Section[0])
return (
<View>
{intialRenderState.map(item => <Text key={item.id}>{item.text}</Text>)}
<Pressable onPress={()=> setInitialRenderState(prevState => [...prevState, Data_Section[prevState.length]])}>
<Text>Add item</Text>
<Pressable>
</View>
)
}
const DATA_Section = [
{
id: 0,
text: "Some text1",
},
{
id: 1,
text: "Some text2",
},
{
id: 2,
text: "Some text 3",
},
];
const sample = () => {
const [ index, setIndex ] = useState(0);
const handleChangeIndex = () => {
if (index < DATA_Section.length) {
setIndex(index + 1);
}else{
setIndex(0);
}
}
return(
<View style={{flex:1}}>
<TouchableWithoutFeedback onPress={()=>handleChangeIndex()}>
<View style={{width:100, height:50, backgroundColor:'red'}}>
<Text>
{DATA_Section[index].text}
</Text>
</View>
</TouchableWithoutFeedback>
</View>
);
}
I'm trying to keep track of a list of chosen exercises, and then render 'added' if the chosen exercise is in the list, or 'not added' if it's not in the list. I add the exercise to chosenExerciseArray when it's not already included, and remove it if it is. When I use console.log(chosenExerciseArray) it correctly displays the list, but if I use setChosenExercises(chosenExerciseArray) the state does not get updated correctly, and the chosenExerciseArray somehow stops logging the correct array as well.
I've also tried adding chosenExerciseArray as a useEffect dependency, but that causes an infinite loop. I'm really not sure what's going on here, but I believe it's an issue with my understanding of state.
EDIT: The TouchableOpacity is inside of a map, which may also be the issue, but I'm not sure how
<View style={styles.exerciseheadericongroup}>
<TouchableOpacity
onPress={() => {
if(!chosenExerciseArray.includes(exercise.id)) {
chosenExerciseArray.push(exercise.id);
} else {
for(var i=0; i<chosenExerciseArray.length; i++) {
if(chosenExerciseArray[i] === exercise.id) {
chosenExerciseArray.splice(i, 1);
}
}
}
console.log(chosenExerciseArray);
}}
>
{chosenExercises.includes(exercise.id) ? (
<Text>added</Text>
) : (
<Text>not added</Text>
)}
</TouchableOpacity>
</View>
As stated in the React documentation
state is a reference to the component state at the time the change is being applied. It should not be directly mutated. Instead, changes should be represented by building a new object based on the input from state and props. [link]
I wish I could explain it better, but basically it means that manipulating chosenExerciseArray with .push and then executing setChosenExerciseArray(chosenExerciseArray) messes up the state handling. You need to make a copy of chosenExerciseArray and manipulate it instead, and then use the manipulated copy to set the new state. I have created two examples for you to look at:
Example 1 uses a different logic, where added is part of the exercise object itself, such that that the chosenExerciseArray is not necessary to keep track of whether the exercise is added or not. This is also a bit more efficient, because you do not need to check if an exercise is in the chosenExerciseArray or not.
import React, { useState } from "react";
import { Pressable, StyleSheet } from "react-native";
import { Text, View } from "../components/Themed";
interface Exercise {
id: string;
name: string;
added: boolean;
}
const updateExerciseArray = (exercises: Exercise[], exercise: Exercise) => {
// Shallow copy
const updatedExercises = [...exercises];
// Manipulate the copy
return updatedExercises.map((updatedExercise) => {
if (updatedExercise.id === exercise.id) {
// Shallow copy of object in array where added is negated
return { ...updatedExercise, added: !updatedExercise.added };
}
return updatedExercise;
});
};
export default function Example1() {
const [exercises, setExercises] = useState<Exercise[]>([
{ id: "1", name: "Pushups", added: false },
{ id: "2", name: "Situps", added: false },
{ id: "3", name: "Squats", added: false },
{ id: "4", name: "Pullups", added: false },
{ id: "5", name: "Leg Raises", added: false },
{ id: "6", name: "Plank", added: false },
{ id: "7", name: "Burpees", added: false },
{ id: "8", name: "Jumping Jacks", added: false },
{ id: "9", name: "Wall Sit", added: false },
]);
return (
<View style={styles.container}>
{exercises.map((exercise) => {
return (
<Pressable
key={exercise.id}
onPress={() => {
setExercises(updateExerciseArray(exercises, exercise));
}}
style={styles.row}
>
<Text style={styles.text}>{exercise.name}</Text>
<Text style={styles.text}>
{exercise.added ? "Added" : "Not added"}
</Text>
</Pressable>
);
})}
</View>
);
}
const styles = StyleSheet.create({
container: {
paddingHorizontal: 50,
flex: 1,
backgroundColor: "#fff",
justifyContent: "center",
},
row: {
marginVertical: 10,
flexDirection: "row",
justifyContent: "space-between",
},
text: {
fontSize: 20,
},
});
Example 2 uses a solution that fits your current implementation.
import React, { useState } from "react";
import { Pressable, StyleSheet } from "react-native";
import { Text, View } from "../components/Themed";
interface Exercise {
id: string;
name: string;
}
const updateExerciseArray = (
chosenExerciseArray: string[],
exerciseToChange: Exercise
) => {
// Shallow copy
const updatedChosenExerciseArray = [...chosenExerciseArray];
if (updatedChosenExerciseArray.includes(exerciseToChange.id)) {
// Remove the exercise from the array
return updatedChosenExerciseArray.filter(
(exerciseId) => exerciseId !== exerciseToChange.id
);
} else {
// Append the exercise to the array
return [...updatedChosenExerciseArray, exerciseToChange.id];
}
};
export default function Example2() {
const [exercises] = useState<Exercise[]>([
{ id: "1", name: "Pushups" },
{ id: "2", name: "Situps" },
{ id: "3", name: "Squats" },
{ id: "4", name: "Pullups" },
{ id: "5", name: "Leg Raises" },
{ id: "6", name: "Plank" },
{ id: "7", name: "Burpees" },
{ id: "8", name: "Jumping Jacks" },
{ id: "9", name: "Wall Sit" },
]);
const [chosenExerciseArray, setChosenExerciseArray] = useState<string[]>([
"1",
"2",
]);
return (
<View style={styles.container}>
{exercises.map((exercise) => {
return (
<Pressable
key={exercise.id}
onPress={() => {
setChosenExerciseArray(
updateExerciseArray(chosenExerciseArray, exercise)
);
}}
style={styles.row}
>
<Text style={styles.text}>{exercise.name}</Text>
<Text style={styles.text}>
{chosenExerciseArray.includes(exercise.id)
? "Added"
: "Not added"}
</Text>
</Pressable>
);
})}
</View>
);
}
const styles = StyleSheet.create({
container: {
paddingHorizontal: 50,
flex: 1,
backgroundColor: "#fff",
justifyContent: "center",
},
row: {
paddingVertical: 10,
flexDirection: "row",
justifyContent: "space-between",
},
text: {
fontSize: 20,
},
});
export default class App extends Component {
constructor() {
super();
// flatlist data
this.state = {
data: [
{ key: "Skptricks", data: "one", name: "one" },
{ key: "Sumit" , data: "two" , name: "one"},
{ key: "Amit" , data: "three", name: "one"},
],
}
}
// flat list
render() {
return (
<View>
<FlatList
data={this.state.data}
renderItem={({item}) => <Text>{item.name}</Text>
/>
</View>
);
}
}
Can you explain what exactly do you need to print ? Please refer the code. I'm printing the 'name' item of your array. If you want to print 'data' items , use item.data inside renderItem. Let me know if you need any help.
const DATA = [{ key: "Skptricks", data: "one", name: "one" },
{ key: "Sumit" , data: "two" , name: "one"},
{ key: "Amit" , data: "three", name: "one"},
];
const Item = ({ name }) => (
<View style={styles.item}>
<Text style={styles.name}>{name}</Text>
</View>
);
const App = () => {
const renderItem = ({ item }) => (
<Item name={item.name} />
);
return (
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: StatusBar.currentHeight || 0,
},
item: {
backgroundColor: '#f9c2ff',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
name: {
fontSize: 32,
},
});
I want to show the sidebar content in a different background color.For that I've tried TouchableOpacity underlay.But that is not the one I'm looking for.After giving TouchableOpacity ,it will change the color of the text only not the entire list background.How do I change the listitem background color as I'm using native base ui kit.Please help.Is there any method to do that?This is how the sidebar looks like.I've done something likes the following.Setting pressStatus as true within onPresList and if it is true change backround color.But navigation to route is not working.There is a mistake
https://i.stack.imgur.com/w9YiR.png
How do I change background color onPress? Following is my code.
updated
import React, { Component } from "react";
import { Image, FlatList } from "react-native";
import {
Content,
Text,
List,
ListItem,
Icon,
Container,
Left,
Right,
Badge,
Thumbnail
} from "native-base";
import styles from "./style";
const drawerCover = require("../../imgs/quwait.jpg");
const datas = [
{
name: "Dashboard",
route: "Anatomy",
icon: require("../../imgs/dashboard.png"),
},
{
name: "Companies",
route: "Header",
icon: require("../../imgs/enterprise1.png"),
},
{
name: "Company Admin",
route: "Footer",
icon: require("../../imgs/icon1.png"),
},
{
name: "Employee",
route: "NHBadge",
icon: require("../../imgs/businessman1.png"),
},
{
name: "Employs",
route: "NHButton",
icon: require("../../imgs/employee1.png"),
},
{
name: "Announcement",
route: "NHCard",
icon: require("../../imgs/megaphone1.png"),
},
{
name: "Holiday",
route: "Check",
icon: require("../../imgs/sun-umbrella1.png"),
},
{
name: "Accounting Report",
route: "NHTypography",
icon: require("../../imgs/accounting1.png"),
},
{
name: "Invoice",
route: "NHCheckbox",
icon: require('../../imgs/approve-invoice1.png'),
},
{
name: "Settings",
route: "NHDatePicker",
icon: require('../../imgs/settings1.png'),
},
{
name: "Safety Phone Numbers",
route: "NHThumbnail",
icon: "user",
},
{
name: "NBK",
route: "NHDeckSwiper",
icon: "swap",
},
{
name: "ABK",
route: "NHFab",
icon: "help-buoy",
},
{
name: "CBK",
route: "NHForm",
icon: "call",
},
{
name: "Daily Invoice",
route: "NHIcon",
icon: "information-circle",
},
{
name: "Kolin",
route: "NHLayout",
icon: "grid",
},
{
name: "Limak",
route: "NHList",
icon: "lock",
},
{
name: "Polaytol",
route: "ListSwipe",
icon: "code-working",
},
{
name: "ACTS",
route: "NHPicker",
icon: "arrow-dropdown",
}
];
class SideBar extends Component {
constructor(props) {
super(props);
this.state = {
shadowOffsetWidth: 1,
shadowRadius: 4,
pressStatus:false
};
}
onPressList = (DATA, INDEX) => {
this.props.navigation.navigate(DATA.route);
this.setState({ pressStatus : true, selectedItem: INDEX});
}
render() {
return (
<Container>
<Content
bounces={false}
style={{ flex: 1, backgroundColor: "#fff", top: -1 }}
>
<Image source={drawerCover} style={styles.drawerCover} />
<FlatList
data={datas}
keyExtractor={(item, index) => String(index)}
renderItem={({ DATA, INDEX }) => {
<ListItem
button
noBorder
onPress={() => this.onPressList(DATA, INDEX)}
style={{
backgroundColor:
this.state.selectedItem === INDEX ? "#cde1f9" : "transparent"
}}
>
<Left>
<Image
source={DATA.icon }
style={{width:30,height:30}}
/>
<Text style={styles.text}>
{DATA.name}
</Text>
</Left>
</ListItem>}}
/>
</Content>
</Container>
);
}
}
export default SideBar;
In the App example from native Base they don't support styles for background items list. So you should change your List component from NativeBase and add a FlatList Component from react native. But you should also return the ListItem component from NativeBase and don't forget the import { FlatList } from "react-native";
You should also modify the onPressList function (I would transform it into an arrow function)
In your states you need to add the state selectedItem: 0
Everytime you press an item, your function would be called by modifying a selectedItem idex, which tells the Flatlist, which Item should get which background. I think this has to be the solution.
If it doesn't compile, make sure that you support arrow functions and that any curly braces or something like that isn't missing.
Final Code UPDATE
import React, { Component } from "react";
import { Image, FlatList } from "react-native";
import {
Content,
Text,
List,
ListItem,
Icon,
Container,
Left,
Right,
Badge,
Thumbnail
} from "native-base";
import styles from "./style";
const drawerCover = require("../../imgs/quwait.jpg");
const datas = [
{
name: "Dashboard",
route: "Anatomy",
icon: require("../../imgs/dashboard.png"),
},
{
name: "Companies",
route: "Header",
icon: require("../../imgs/enterprise1.png"),
},
{
name: "Company Admin",
route: "Footer",
icon: require("../../imgs/icon1.png"),
},
{
name: "Employee",
route: "NHBadge",
icon: require("../../imgs/businessman1.png"),
},
{
name: "Employs",
route: "NHButton",
icon: require("../../imgs/employee1.png"),
},
{
name: "Announcement",
route: "NHCard",
icon: require("../../imgs/megaphone1.png"),
},
{
name: "Holiday",
route: "Check",
icon: require("../../imgs/sun-umbrella1.png"),
},
{
name: "Accounting Report",
route: "NHTypography",
icon: require("../../imgs/accounting1.png"),
},
{
name: "Invoice",
route: "NHCheckbox",
icon: require('../../imgs/approve-invoice1.png'),
},
{
name: "Settings",
route: "NHDatePicker",
icon: require('../../imgs/settings1.png'),
},
{
name: "Safety Phone Numbers",
route: "NHThumbnail",
icon: "user",
},
{
name: "NBK",
route: "NHDeckSwiper",
icon: "swap",
},
{
name: "ABK",
route: "NHFab",
icon: "help-buoy",
},
{
name: "CBK",
route: "NHForm",
icon: "call",
},
{
name: "Daily Invoice",
route: "NHIcon",
icon: "information-circle",
},
{
name: "Kolin",
route: "NHLayout",
icon: "grid",
},
{
name: "Limak",
route: "NHList",
icon: "lock",
},
{
name: "Polaytol",
route: "ListSwipe",
icon: "code-working",
},
{
name: "ACTS",
route: "NHPicker",
icon: "arrow-dropdown",
}
];
class SideBar extends Component {
constructor(props) {
super(props);
this.state = {
shadowOffsetWidth: 1,
shadowRadius: 4,
pressStatus:false,
selectedItem:0
};
}
onPressList = (data, index) => {
this.props.navigation.navigate(data.route);
this.setState({ pressStatus : true, selectedItem: index});
}
render() {
return (
<Container>
<Content
bounces={false}
style={{ flex: 1, backgroundColor: "#fff", top: -1 }}
>
<Image source={drawerCover} style={styles.drawerCover} />
<FlatList
data={datas}
keyExtractor={(item, index) => String(index)}
extraData={this.state.selectedItem}
renderItem={({item:data, index}) => {
const { selectedItem: sd } = this.state
const localColor ={backgroundColor: sd === index ? "#cde1f9" : "transparent"}
return (
<ListItem
button
noBorder
style={localColor}
onPress={() => this.onPressList(data, index)}
>
<Left>
<Image
source={data.icon}
style={{width:30,height:30}}
/>
<Text style={styles.text}>
{data.name}
</Text>
</Left>
</ListItem>
)
}}
/>
</Content>
</Container>
);
}
}
export default SideBar;
I'm trying to loop over a simple array of objects and display the content inside Text tag. The looping is being done but the actual content is not displayed on the screen.
import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
export default class App extends React.Component {
render() {
const initialArr = [
{
id: 1,
color: "blue",
text: "text1"
},
{
id: 2,
color: "red",
text: "text2"
},
{
id: 3,
color: "green",
text: "text3"
}
];
return (
<View style={styles.container}>
{initialArr.map((prop, key) => {
return (
<Text key={key}>{prop[1]}</Text>
);
})}
</View>
);
}
}
render() {
const initialArr = [
{ id: 1, color: "blue", text: "text1" },
{ id: 2, color: "red", text: "text2" },
{ id: 3, color: "green", text: "text3"}
];
var textInputComponents = initialArr.map((prop, key) => {
return (
<Text key={key}>{prop.text}</Text>
);
})
return (
<View style={styles.container}>
{textInputComponents}
</View>
);
}
Apart from John_mc's answer, instead of using inline mapping, you can also split the mapping operation, to improve readability and maintainability