Javascript || React Native how to use map() [duplicate] - javascript

This question already has an answer here:
React native How to add Images with map()
(1 answer)
Closed 11 months ago.
import React from "react";
import { View, Text, StyleSheet, ScrollView, Image } from "react-native";
const Icons = [
{ name: "Food", uri: require("./images/Food.png") },
{ name: "Mart", uri: require("./images/mart.png") },
{ name: "Car", uri: require("./images/car.png") }
];
const IconSelection = Icons.map((icons) => (
<View>
<Image source={icons.uri} />
<Text style={{ textAlign: "center" }}>{icons.name}</Text>
</View>
));
const styles = StyleSheet.create({});
export default IconSelection;
Is my way of adding images to my const Icons correct? Basically I want to create like a list of Icons using images and able to call them. Previously my method is basically handcode them but I found it is very messy. I think maps() could help me but I'm not really sure how to use it too. Thank you.
import { StatusBar } from "expo-status-bar";
import { StyleSheet, Text, View } from "react-native";
import IconSelection from "./icons";
export default function App() {
return (
<View style={styles.container}>
<Text>Open up App.js to start working on your app!</Text>
<IconSelection />
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
});
It says Check the render method of app. The error source is pointing require().

You are returning an array of items.
You most likely need a new component that wraps your icons. Something like this
const IconSelection = () => {
return <>
Icons.map((icons) => (
<View>
<Image source={icons.uri} />
<Text style={{ textAlign: "center" }}>{icons.name}</Text>
</View>
)
</>
}
You also need to make sure that the paths to images are correct and you have proper loaders for that file type.

Related

React Native: How to create elements and return them in a function

I am new to React Native
and I want to create elements and return them with a button onPress function, I donĀ“t want to hide and show like a Modal, else create them.
import React from "react"
import { Button, StyleSheet, Text, View } from 'react-native';
function createElement() {
return(
<View style={styles.elementStyle}>
<Text style={styles.txt}>ELement</Text>
</View>
)
}
const App = () => {
return (
<View style={{ flex: 1,backgroundColor: '#fff', alignItems: 'center',justifyContent: 'center',}}>
<Button title="create element" onPress={() => createElement()}/>
</View>
);
}
export default App;
const styles = StyleSheet.create({
container: {flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center',
},
elementStyle: { backgroundColor:'grey', width:'95%', height: 90, margin: 10, justifyContent: "center", borderRadius: 10, fontWeight: "bold" },
txt: {textAlign:'center',fontSize:28,color:'#fff',fontWeight: "bold"}});
I tried with functions that return components, but nothing works
Do you want to have multiple elements or just a single modal?
For multiple elements, do the below. For a single element, it's easiest to just use show / hide logic.
The best way to do this is have an array in state like so:
const [elementArray, setElementArray] = useState();
Your createElement method instead should become two parts, adding elements to the array with the content you want, which you can then render in the main return function with a map method.
const addElement = () => {
// Just using text here. If you want a more complex element, you will have to add things to the object.
const newElementText = 'Element';
const newElementArray = elementArray.slice();
newElementArray.push(newElementText);
setElementArray([...newElementArray]);
}
Then in your return function in the component:
{elementArray.map((element) => {
return (
<View style={styles.elementStyle}>
<Text style={styles.txt}>element</Text>
</View>
);
}
)}
Make sure you add a useEffect hook so the component rerenders when you add a new element:
useEffect(()=> {}, [elementArray])
You can't navigate to a component like that. If you are making it so your component appears on the click of a button I suggest building a Stack by importing react-native/navigation. Then, building your structure as shown. My explanation might not have been the best because your initial code was unstructured. This should give you an even better answer. docs
const navigation = useNavigation();
function createElement() {
return(
<View style={styles.elementStyle}>
<Text style={styles.txt}>Element</Text>
</View>
)
}
function Home() {
return (
<View style={{ flex: 1,backgroundColor: '#fff', alignItems: 'center',justifyContent: 'center',}}>
<Button title="create element" onPress={() => navigation.navigate("Element")}/>
</View>
);
}
const App = () => {
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Element" component={CreateElement} />
</Stack.Navigator>
}

Get total number of items rendered from FlatList, and then update that number based on item state changes

I'm very new to React Native (and React/Javascript in general to be honest) and am very stuck. I have a chore tracking app that renders a list of chores using Flatlist, that can then be swiped/checked off using React Native Gesture Handler Swipeable.
I want a tag above the chore list that shows the total number of chores completed (the placeholder currently says "?? Chores complete"). I know this entails finding 1) How many of my ChoreListItems are rendered from the Flatlist component and then 2) How many of those items have a state of "isComplete". I have several nested/reusable components since there will be several versions of this screen (Laundry, kitchen, bathroom, etc.) and I know that's making it even more confusing for me. I feel like there's probably an obvious answer here, but I'm lost on how to even begin unpacking this.
Here is the ChoreListItem (what is rendered from Flatlist and able to be swiped):
import React, { useState, useRef} from 'react';
import { Animated, Image, StyleSheet, Text, TouchableWithoutFeedback, View } from 'react-native';
import Swipeable from 'react-native-gesture-handler/Swipeable';
import appStyles from '../config/appStyles';
import colors from '../config/colors';
import BodyText from '../config/BodyText';
function ChoreListItem({title}) {
const [isComplete, setIsComplete] = useState(false);
const swipeableRef = useRef();
//Track interaction occurs on left swipe
const LeftActions = (progress, dragX) => {
const scale = dragX.interpolate({
inputRange: [0, 100],
outputRange: [0,1],
extrapolate: 'clamp',
});
if (isComplete == false) {
return (
<View style ={[appStyles.card, styles.leftActions]}>
<Animated.Text style={[styles.swipeText, {transform: [{scale}], fontSize:16, }]}>Swipe to track</Animated.Text>
<Image source={require('../assets/control_arrow_right.png')} style={{alignItems:'center',tintColor:colors.baseWhite,}}/>
</View>
);
}
};
//Untrack button renders on right swipe
const RightActions = (progress, dragX) => {
const scale = dragX.interpolate({
inputRange: [-100,0],
outputRange: [1,0],
extrapolate: 'clamp',
});
if (isComplete === true) {
return (
<TouchableWithoutFeedback onPress={closeSwipeable}>
<View style ={[appStyles.card, styles.rightActions]}>
<Animated.Text style={[styles.swipeText,{transform: [{scale}], fontSize:16, }]}>Tap to untrack</Animated.Text>
</View>
</TouchableWithoutFeedback>
);
}
};
//Closes swiped action and changes state
const closeSwipeable = () => {
if (isComplete===false) {
setIsComplete (true);
console.log(title + ' tracked');
} else {
setIsComplete(false);
console.log(title + ' untracked');
}
}
return (
<Swipeable
ref={swipeableRef}
state={isComplete}
renderLeftActions={LeftActions}
leftThreshold={20}
rightThreshold={10}
overshootRight={false}
renderRightActions={RightActions}
onSwipeableLeftOpen={closeSwipeable}
>
<View style={[appStyles.card, styles.choreListItem]}>
<BodyText style={{textDecorationLine: isComplete ? "line-through" : "none"}}>{title}</BodyText>
<Image style={[styles.checkmark, {display: isComplete ? "flex" : "none"}]} source={require('../assets/checkmark_large.png')}/>
</View>
</Swipeable>
);
}
export default ChoreListItem;
const styles = StyleSheet.create({
checkmark: {
width:16,
height:16,
},
choreListItem: {
paddingLeft:16,
paddingRight:16,
paddingTop:20,
paddingBottom:20,
marginBottom:16,
flex:1,
flexDirection:'row',
alignItems:'center',
justifyContent:'space-between'
},
swipeText: {
color: colors.baseWhite,
},
leftActions: {
paddingLeft:16,
paddingRight:16,
paddingTop:20,
paddingBottom:20,
marginBottom: 16,
backgroundColor: colors.primaryBlue,
flex: 1,
shadowColor: 'transparent',
alignItems:'center',
flexDirection:'row'
},
rightActions: {
paddingLeft:16,
paddingRight:16,
paddingTop:20,
paddingBottom:20,
marginBottom: 16,
backgroundColor: colors.primaryPurple,
shadowColor: 'transparent',
alignItems:'flex-end',
flexDirection:'row',
},
});
Here is the ChoreList (includes the Flatlist component):
import React from 'react';
import {FlatList, StyleSheet, Text, View} from 'react-native';
import appStyles from '../config/appStyles';
import colors from '../config/colors';
import ChoreListItem from '../components/ChoreListItem';
import SectionTitleBar from '../components/SectionTitleBar';
function ChoreList({getCategory}) {
return (
<View style={appStyles.containerPadding}>
{/* <SectionTitleBar title="Today"/> */}
<View>
<FlatList
data = {getCategory}
renderItem={({item}) =>
<ChoreListItem title={item.title} />
}
keyExtractor={(item) => item.title.toString()}
/>
</View>
</View>
);
}
export default ChoreList;
const styles = StyleSheet.create({
choreListItem: {
padding:16,
marginBottom:16,
},
});
Here is the component/screen with all of the props:
import React from 'react';
import { Text, View } from 'react-native';
import choreCats from '../config/choreCats';
import choreItems from '../config/choreItems';
import colors from '../config/colors';
import ChoreList from '../components/ChoreList';
import PageHeader_TitleIllo from '../components/pageHeaders/PageHeader_TitleIllo';
import Screen from '../components/Screen';
function LaundryChoresScreen() {
const choreCategory = choreCats[4];
return (
<Screen>
<PageHeader_TitleIllo
category={choreCategory.category}
image={choreCategory.image}
bgColor={choreCategory.bgColor}
/>
<Text style={{padding:8, backgroundColor:colors.blueHueMedium, alignSelf:'flex-start', marginTop: 8, marginBottom: 8}}>?? Chores complete</Text>
<View style={{backgroundColor:colors.baseFog,}}>
<ChoreList getCategory={choreItems.filter(({category})=>category=== "laundry")}/>
</View>
</Screen>
);
}
export default LaundryChoresScreen;
There are a couple of different ways to accomplish this, but probably the best is for you to lift the state of your completed chores up to the level where it's shared with this new functionality. You would do that by tracking the complete/incomplete quality in the highest place where it needs to be shared. Currently, that's your LaundryChoresScreen component.
You would then need to pass the function that changes your array of chores down to the props of the ChoreListItem to invoke whenever they need to change their chore's status. This process is called prop drilling. A lot of people are frustrated with the tedium of prop drilling, and will opt to create a global state to manage things from anywhere in the application (using Contexts or Redux), but those are probably overkill for this case.

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

Using react-native-modalbox in ListView causes modalbox to only fill the list item space instead of full screen?

When I use the package react-native-modalbox with a FlatList (each list item can spawn a distinct modal when tapped), the modal that is spawned only fills the area of the list item instead of going full screen like it normally should.
A working snack that shows the issue is here:
https://snack.expo.io/BkICbjwWQ
For completeness I'll paste the code in here as well:
import React, { Component } from 'react';
import { Text, View, StyleSheet, FlatList, Button } from 'react-native';
import { Constants } from 'expo';
import Modal from "react-native-modalbox";
// You can import from local files
import AssetExample from './components/AssetExample';
// or any pure javascript modules available in npm
import { Card } from 'react-native-elements'; // Version can be specified in package.json
export default class App extends Component {
render() {
let myRefs = [];
return (
<View style={styles.container}>
<FlatList
data={[{key: 'a'}, {key: 'b'}]}
renderItem={({item}) => <View>
<Modal
style={[styles.modal]}
ref={(modalItem) => {myRefs[item.key] = modalItem;} }
swipeToClose={true}
onClosed={this.onClose}
onOpened={this.onOpen}
onClosingState={this.onClosingState}>
<Text style={styles.text}>Basic modal</Text>
</Modal><Text>{item.key}</Text><Button title="Basic Modal" onPress={() => myRefs[item.key].open()} style={styles.btn}>Basic modal</Button></View>}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
},
});
I basically have the same question/problem as (react-native-modalbox stuck in child component context) but there was no answer to that question and he did not provide enough details with a minimum working example.
Your modal component is inside the rendered item. This causes it to be bound to the item. Although you can fix this issue by using appropriate props or some custom styling, this is not efficient. You would have 1000 modal components if you had 1000 items in your list.
You should move out your modal component and make it sibling to the FlatList. This way you would have only single modal. You can change the contents of the modal with a state value.
Sample
export default class App extends Component {
render() {
let myRefs = [];
return (
<View style={styles.container}>
<Modal
style={[styles.modal]}
ref={modalItem => { myRefs['modal'] = modalItem; }}
swipeToClose={true}
onClosed={this.onClose}
onOpened={this.onOpen}
onClosingState={this.onClosingState}>
<Text style={styles.text}>Basic modal</Text>
</Modal>
<FlatList
data={[{ key: 'a' }, { key: 'b' }]}
renderItem={({ item }) => (
<View>
<Text>{item.key}</Text>
<Button
title="Basic Modal"
onPress={() => myRefs['modal'].open()}
style={styles.btn}>
Basic modal
</Button>
</View>
)}
/>
</View>
);
}
}

TabBarIOS in ReactNative not working, items overlapping each other

So im making an app and this is my code:
import React, { Component } from 'react';
import {
View,
Text,
TabBarIOS,
StyleSheet,
Dimensions
} from 'react-native';
//import Styles from './LayoutStyle.js';
class Layout extends Component {
constructor(){
super()
this.state = {selectedTab: 'tabThree'}
}
setTab(tabId){
this.setState({selectedTab: tabId})
}
render() {
return(<View style={Styles.Layout}>
<TabBarIOS>
<TabBarIOS.Item
systemIcon='history'
selected={this.state.selectedTab === 'tabOne'}
onPress={() => this.setTab('tabOne')}>
<View>
<Text>Jure1</Text>
</View>
</TabBarIOS.Item>
<TabBarIOS.Item
systemIcon='bookmarks'
selected={this.state.selectedTab === 'tabTwo'}
onPress={() => this.setTab('tabTwo')}>
<View>
<Text>Jure2</Text>
</View>
</TabBarIOS.Item>
<TabBarIOS.Item
systemIcon='more'
selected={this.state.selectedTab === 'tabThree'}
onPress={() => this.setTab('tabThree')}>
<View>
<Text>Jure3</Text>
</View>
</TabBarIOS.Item>
</TabBarIOS>
</View>
);
}
}
const Styles = StyleSheet.create({
Layout: {
alignItems: "center",
justifyContent: "center",
height: Dimensions.get('window').height,
width: Dimensions.get('window').width,
},
TabBar: {
backgroundColor: 'grey'
}
});
export default Layout;
Well what i expected was an app where you have a TabBar on the bottom with three different items to choose from and it should look like i would in a native ios app. Well thats not the case, what i get is this:
Well what should i do? How do i style this item to not overlap? Any ideas?
The layout style is causing the inner content to get centred oddly, change Layout style to this:
Layout: {
flex:1,
}
Additionally, when trying to draw a scene from the tab clicked you will want to use a render function inside the TabBarIOS.Item object, react native provides some good examples of how to do this in the documentation: https://facebook.github.io/react-native/docs/tabbarios.html
I would highly recommend placing a navigator for each object which allows you to have much more control over the scene changes:
<TabBarIOS.Item
systemIcon='history'
title= 'Jure1'
selected={this.state.selectedTab === 'tabOne'}
onPress={() => this.setTab('tabOne')}>
<View style = {{flex:1}}>
<Navigator
renderScene={this._renderScene}
/>
</View>
</TabBarIOS.Item>

Categories

Resources