Rendering with map twice leads to error in React Native - javascript

<View style={styles.card} >
{store.crud.list.map(function(element, index){
return (
<View style={styles.wrapper}>
{element.map(function(number, index){
return(
<View>
<Text style={styles.number}>
{element}
</Text>
</View>
);
})}
</View>
);
})}
</View>
I have this react-native element and it seems that it won't render, the second render seems to break the code, because I remove it and just render element, it works fine.
I am getting the error:
TypeError: "Text constructor: 'new' is required"
React 12
unstable_runWithPriority scheduler.development.js:643
React 6
Also, the content of store.crud is { list: [[2,3,3,3,3,4],[3,1,1,1,1,1]] }. It's simple number refers to the number and element refers to the array.
The following works fine:
<View style={styles.card} >
{store.crud.list.map(function(element, index){
return (
<View style={styles.wrapper}>
{element}
</View>
);
})}
</View>
However, as I've said, I want to style every number inside element.
EDIT: I also tried this and it doesn't work even though it works on Codesandbox:
const ConfigScreen = () => {
let store = {
crud:{
list:[[1,2,3,4,5,6],[1,23,45,65,8]]
}
};
return (
<View style={styles.card} >
{store.crud.list.map(function (element, index) {
return (
<View style={styles.wrapper}>
{element.map(function (number, index) {
return (
<View>
<Text style={styles.number}>
{number}
</Text>
</View>
);
})}
</View>
);
})}
</View>
);
};
Is it an issue with the expo client/metro bundler?
I entered expo start --no-https and then clicked on "Run in web browser".

Try this
import React, { Component } from "react";
import { StyleSheet, Text, View } from "react-native";
class App extends Component {
store = {
crud:{
list:[[1,2,3,4,5,6],[1,23,45,65,8]]
}
};
render() {
return (
<View style={styles.card} >
{this.store.crud.list.map(function (element, index) {
return (
<View style={styles.wrapper}>
{element.map(function (number, index) {
return (
<View>
<Text style={styles.number}>
{number}
</Text>
</View>
);
})}
</View>
);
})}
</View>
);
}
}
const styles = StyleSheet.create({
app: {
marginHorizontal: "auto",
maxWidth: 500
},
logo: {
height: 80
},
header: {
padding: 20
},
title: {
fontWeight: "bold",
fontSize: "1.5rem",
marginVertical: "1em",
textAlign: "center"
},
text: {
lineHeight: "1.5em",
fontSize: "1.125rem",
marginVertical: "1em",
textAlign: "center"
},
link: {
color: "#1B95E0"
},
code: {
fontFamily: "monospace, monospace"
}
});
export default App;
This code works as . Also, you can try to run this code on this site https://codesandbox.io/s/q4qymyp2l6?file=/src/App.js

Try to use flat list, see expo example - https://docs.expo.io/versions/latest/react-native/flatlist/

Related

React Native TextInput Value persist when Tab is changed

I have encountered a weird issue in the newest react native where the value in the text input in a component remains when a tab is switched.
I can't figure what is going on and I thought it should re-render when tab is changed but it doesn't.
Here's my code
app.js
export default function App() {
const [tab, setTab] = useState("TAB1")
return (
<View style={styles.container}>
<View style={{ flexDirection: 'row' }}>
<TouchableOpacity
style={{ borderRadius: 5, borderWidth: 1, marginRight: 5, padding: 20 }}
onPress={() => setTab("TAB1")}
>
<Text>Tab 1</Text>
</TouchableOpacity>
<TouchableOpacity
style={{ borderRadius: 5, borderWidth: 1, padding: 20}}
onPress={() => setTab("TAB2")}
>
<Text>Tab 2</Text>
</TouchableOpacity>
</View>
<View style={{ marginTop: 20}}>
{
tab === "TAB1" ? (
<View>
<InputComponent tab={tab} />
</View>
) : (
<View>
<InputComponent tab={tab} />
</View>
)
}
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
marginTop: 100,
padding: 10
},
});
inputcomponent.js
export function InputComponent({ tab }) {
const [value, setValue] = useState("");
return (
<View>
<Text>{tab}</Text>
<TextInput placeholder="INPUT HERE" value={value} onChangeText={setValue}/>
</View>
)
}
It seems like the input component re-renders but the text input within it doesn't change.
Demo Issue
This is such a good question. This is because we are importing it once and passing it to two different components. It changes the tab but uses the same textinput state because they are using the same key.
To fix this pass in the key prop so React knows that tab changed:
{
tab === "TAB1" ? (
<View>
<InputComponent key={1} tab={tab} />
</View>
) : (
<View>
<InputComponent key={2} tab={tab} />
</View>
)
}
Snack: https://snack.expo.io/mVVLb9uId
Read about keys: https://reactjs.org/docs/lists-and-keys.html#keys

React Native/TypeScript - How to manage state in functional component and apply flexible style with FlatList

Can anyone tell me how to solve these things?
1.if the list is pressed, I'd like to change the background color of the list
beige(#FFF5E7) to white(#FBFBFB)
2.Also, I'd like to change the read value of the Object fales to true with useState
Problem is that if I pressed the list, whole background color of the list will be changed.
index.tsx
import React, { useState } from 'react';
import { Text, View, TouchableOpacity, FlatList } from 'react-native';
import { useNavigation } from '#react-navigation/native';
import { DETAIL } from '../../sample';
import { Object } from './sample';
export default function sample() {
const [state, setState] = useState(Object);
const navigation = useNavigation();
let Element;
const renderItem = ({ item }) => {
const readState = () => {
navigation.navigate(NOTICE_DETAIL);
const readValue = [...state];
let value = { ...readValue[0] };
value.read = true;
readValue[0] = value;
setState(readValue);
};
if (state[0].read) {
Element = (
<TouchableOpacity onPress={readState}>
<View style={[styles.row, { backgroundColor: '#FBFBFB' }]}>
<View style={styles.container}>
<View style={styles.end}>
<Text style={styles.text}>{item.text}</Text>
<Text style={styles.time}>{item.time}</Text>
</View>
<Text style={styles.content}>{item.content}</Text>
</View>
</View>
</TouchableOpacity>
);
} else {
Element = (
<TouchableOpacity onPress={readState}>
<View style={[styles.row, { backgroundColor: '#FFF5E7' }]}>
<View style={styles.container}>
<View style={styles.end}>
<Text style={styles.text}>{item.text}</Text>
<Text style={styles.time}>{item.time}</Text>
</View>
<Text style={styles.content}>{item.content}</Text>
</View>
</View>
</TouchableOpacity>
);
}
return Element;
};
}
return (
<View style={{ flex: 1 }}>
<FlatList
extraData={Object}
data={Object}
renderItem={renderItem}
/>
</View>
);
}
Object.ts
export const Object = [
{
id: 1,
text: 'testtesttest',
content: 'testtesttest'
read: false
},
{
id: 2,
text: 'testtesttest',
content: 'testtesttest'
read: false
}
id: 3,
text: 'testtesttest',
content: 'testtesttest'
read: false
}
]
We can add inline styles into the component style props along with static styles,
For Example:
<TouchableOpacity onPress={readState}>
<View style={[styles.row, { backgroundColor: item.read ? '#FBFBFB' : 'FFF5E7' }]}>
<View style={styles.container}>
<View style={styles.end}>
<Text style={styles.text}>{item.text}</Text>
<Text style={styles.time}>{item.time}</Text>
</View>
<Text style={styles.content}>{item.content}</Text>
</View>
</View>
</TouchableOpacity>
Problem in your readState function and if (state[0].read) {. You always change item with index 0 and then check state[0].read for all elements of your array.

Error: Looks like you nested a 'Navigation Container' inside another when calling a GoToButton in child file React Navigation 5

I created a GoToButton as advised by React Navigation v5 to move from one child screen to another
function GoToButton({ screenName }) {
const navigation = useNavigation();
return (
<TouchableOpacity
title={`${screenName}`}
onPress={() => navigation.navigate(screenName)} style={styles.buttonLogin}>
<Text style={{color: '#ffcc00', fontWeight: 'bold'}}>Start!</Text>
</TouchableOpacity>
);
}
I wanted to move from the login screen which was imported inside of the function:
function LoginScreen({navigation}) {
return (
<View>
<Login />
<View style={{alignContent:'center' , alignItems:'center'}}>
<GoToButton screenName="TabNavigator" />
</View>
</View>
);
}
To the TabNavigatorScreen:
function TabNavigatorScreen({ navigation }) {
return (
<TabNavigator/>
);
}
When I place the reference to the GoToButton like this, the reference works:
function LoginScreen({navigation}) {
return (
<View>
<Login /> //imported earlier from the file Login.js
<View style={{alignContent:'center' , alignItems:'center'}}>
<GoToButton screenName="TabNavigator" /> //next to the imported Login
</View>
</View>
);
}
But my goal is to place the GoToButton inside of the Login.js as shown:
import GoToButton from '../navigation/SwitchNavigator' class Login extends React.Component {
login = () => {
this.props.login(this.props.email)
}
render() {
return (
<View style={{ position: 'absolute', justifyContent: 'center', alignItems: 'center', flex: 1, justifyContents: "flex-end", width: screenWidth, height: screenHeight, backgroundColor: 'white', }}>
<View style={styles.box}>
<TextInput value={this.props.user} style={styles.textarea} onChangeText={input => this.props.updateEmail(input)}
placeholder='Email'/>
<TextInput value={this.props.user} style={styles.textarea} onChangeText={input => this.props.updatePassword(input)}
placeholder='Password' />
<GoToButton screenName="TabNavigator" onPress={() => this.login()}/>
</View>
</View>
);
}
}
Unfortunately, this results in the error: ooks like you nested a 'Navigation Container' inside another. How do I solve this problem?

Multiple Component with Same Function Name in React Native

I'm using react-native-material-menu's popup for showing menu options.
But the issue is, it's not working for multiple scenarios.
I mean when I click on first menu button, the same methods gets triggered and hence the same menu is opened every time.
What should be the better approach for to handle this particular scenario.
_menu = null;
setMenuRef = ref => {
this._menu = ref;
};
hideMenu = () => {
this._menu.hide();
};
showMenu = () => {
this._menu.show();
};
<FlatList
data={this.state.clientsList}
renderItem={({ item }) => {
return (
<View style={styles.caseItem} >
<Card style={styles.card}>
<CardItem>
<Body>
<View style={styles.rowTitle}>
<Text style={styles.title}>{item.FullName}</Text>
<Menu
ref={this.setMenuRef}
button={
<Icon
type="Feather"
name="more-vertical"
onPress={this.showMenu}
style{{fontSize:20,color:'#555'}}
/>
}>
<MenuItem onPress={this.hideMenu}>View</MenuItem>
<MenuItem onPress={this.hideMenu}>Edit</MenuItem>
<MenuItem onPress={this.hideMenu}>Delete </MenuItem>
</Menu>
</View>
<View>
<Text style={styles.lbl}>Email: <Text style={styles.lblValue}>{item.EmailID}</Text></Text>
<Text style={styles.lbl}>Client Type: <Text style={styles.lblValue}>{item.ClientType}</Text></Text>
</View>
</Body>
</CardItem>
</Card>
</View>
);
}}
keyExtractor={item => item.ID}
/>
Snack Here
To handle the states in the correct way, you will need to create a new Class which will be handling just the MenuItem
The below code will work: Here is the Snack.
import * as React from "react";
import { Text, View, StyleSheet } from "react-native";
import Constants from "expo-constants";
import { Container, Content, Card, CardItem, Body, Icon } from "native-base";
import * as Font from "expo-font";
import Menu, { MenuItem, MenuDivider } from "react-native-material-menu";
// You can import from local files
import AssetExample from "./components/AssetExample";
export default class App extends React.Component {
onView = () => {
alert("Do something here");
console.log("You can do what ever you want here");
}
render() {
return (
<View style={styles.container}>
<View style={styles.caseItem}>
<Card style={styles.card}>
<CardItem>
<Body>
<View style={styles.rowTitle}>
<Text style={styles.title}>John Doe</Text>
<CustomMenu onView={this.onView}/>
</View>
<View>
<Text style={styles.lbl}>
Email: <Text style={styles.lblValue}>john#yopmail.com</Text>
</Text>
<Text style={styles.lbl}>
Client Type: <Text style={styles.lblValue}>Individual</Text>
</Text>
</View>
</Body>
</CardItem>
</Card>
</View>
<View style={styles.caseItem}>
<Card style={styles.card}>
<CardItem>
<Body>
<View style={styles.rowTitle}>
<Text style={styles.title}>John Doe</Text>
<CustomMenu onView={this.onView}/>
</View>
<View>
<Text style={styles.lbl}>
Email: <Text style={styles.lblValue}>john#yopmail.com</Text>
</Text>
<Text style={styles.lbl}>
Client Type: <Text style={styles.lblValue}>Individual</Text>
</Text>
</View>
</Body>
</CardItem>
</Card>
</View>
<View style={styles.caseItem}>
<Card style={styles.card}>
<CardItem>
<Body>
<View style={styles.rowTitle}>
<Text style={styles.title}>John Doe</Text>
<CustomMenu onView={this.onView} />
</View>
<View>
<Text style={styles.lbl}>
Email: <Text style={styles.lblValue}>john#yopmail.com</Text>
</Text>
<Text style={styles.lbl}>
Client Type: <Text style={styles.lblValue}>Individual</Text>
</Text>
</View>
</Body>
</CardItem>
</Card>
</View>
</View>
);
}
}
class CustomMenu extends React.Component {
_menu = null;
setMenuRef = ref => {
this._menu = ref;
};
hideMenu = () => {
this._menu.hide();
};
onViewClick = () => {
const {onView} = this.props;
onView();
this.hideMenu();
}
showMenu = () => {
this._menu.show();
};
render() {
return (
<Menu
ref={this.setMenuRef}
button={
<Icon
type="Feather"
name="more-vertical"
onPress={this.showMenu}
style={{ fontSize: 20, color: "#555" }}
/>
}
>
<MenuItem onPress={this.onViewClick}>View</MenuItem>
<MenuItem onPress={this.hideMenu}>Edit</MenuItem>
<MenuItem onPress={this.hideMenu}>Delete </MenuItem>
</Menu>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
paddingTop: Constants.statusBarHeight,
backgroundColor: "#ecf0f1",
padding: 8
},
rowTitle: {
flexDirection: "row",
justifyContent: "space-between",
width: "100%"
},
title: {
fontSize: 14,
marginBottom: 5
},
lbl: {
fontSize: 11,
color: "#000"
},
lblValue: {
fontSize: 11,
color: "#555",
fontWeight: "normal"
},
caseItem: {
marginBottom: 0
}
});
Since the FlatList will iterate over the menu items, you need to maintain index for each iterable menu options.
You can check, you are passing item object within renderItems prop. So you can use the same item.id as a key to your child (iterable) component.
Since the child component now maintains an index, so now you can pass it in methods which will help you differentiate the event which got triggered from the child element.
I hope this might give you an idea about the issue.

react native : how to refactor a function

I have multiple functions that looks almost identical. How can I refactor my function so that I don't have write multiple functions.
This is my code:
renderImage = () => {
var imgSource = this.state.image? imageOne : imageTwo;
return (
<View style={{marginLeft:20, marginTop:20,width:50, height:67, backgroundColor:'transparent', alignItems:'center'}}>
<Image
style={{width:46, height:46}}
source={ imgSource }
/>
<Text style={{paddingTop:4, fontSize:11, color:'#4a4a4a'}}>some text</Text>
</View>
);
}
I have another function that looks very similar:
renderImage2 = () => {
var imgSource = this.state.image2? imageThree : imageFour;
return (
<View style={{marginLeft:20, marginTop:20,width:50, height:67, backgroundColor:'transparent', alignItems:'center'}}>
<Image
style={{width:46, height:46}}
//I want to change source
source={ imgSource }
/>
//Also I want to change text
<Text style={{paddingTop:4, fontSize:11, color:'#4a4a4a'}}>some other text</Text>
</View>
);
}
I just want to change the Image source and the text. Can I do this?
Create another component that returns the render but takes 2 props you pass in (source, text)
import React from 'react';
import { Image, Text, View } from 'react-native';
class ImageWithText extends React.PureComponent {
render() {
const { source, text } = this.props;
return (
<View style={{ marginLeft: 20, marginTop: 20, width: 50, height: 67, backgroundColor: 'transparent', alignItems: 'center' }}>
<Image style={{ width: 46, height: 46 }} source={source} />
<Text style={{ paddingTop: 4, fontSize: 11, color: '#4a4a4a' }}>{text}</Text>
</View>
);
}
}
export default ImageWithText;
and when you want to use the new component
renderImage = () => {
var imgSource = this.state.image? imageOne : imageTwo;
return (
<ImageWithText source={imgSource} text="some text">
);
}
You can first define a renderImage function that takes in a parameter to do decision making. Within this renderImage function, define all the possible images to load within a lookup object, such as below
renderImage = (renderImage) => {
const lookup = {
image_1: { src: imageOne, text: 'text_for_image_one' },
image_2: { src: imageTwo, text: 'text_for_image_two' }
}
const selectedImage = lookup[renderImage] || undefined;
if(!selectedImage) return;
return (
<View style={{marginLeft:20, marginTop:20,width:50, height:67, backgroundColor:'transparent', alignItems:'center'}}>
<Image
style={{width:46, height:46}}
source={selectedImage.src}
/>
<Text style={{paddingTop:4, fontSize:11, color:'#4a4a4a'}}>{selectedImage.text}</Text>
</View>
);
}
Then within your render method do below
render() {
...
{this.renderImage(image_1)}
{this.renderImage(image_2)}
...
}
You can define simple function render like
renderImage = (imageSrc, text) => (
<View style={{marginLeft:20, marginTop:20,width:50, height:67, backgroundColor:'transparent', alignItems:'center'}}>
<Image
style={{width:46, height:46}}
//I want to change source
source={ imageSrc }
/>
//Also I want to change text
<Text style={{paddingTop:4, fontSize:11, color:'#4a4a4a'}}>{text}</Text>
</View>
)
end use in your render like:
{this.renderImage(this.state.image? imageOne : imageTwo, 'some text')}

Categories

Resources