react native : how to refactor a function - javascript

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')}

Related

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.

React Native, Calling a Function from Another one in Render

I have this SMN() function and I've created inside it Site function as Const. So I need to call Site function in Render() function. This is main function code:
SMN() {
const Site = () => {
return (
<View style={{ height: 400 }}>
<WebView source={{ uri: 'https://www.google.com' }} style={{ marginTop: 20 }} />
</View>
);
}
});
This is Render() function where I want to call Site function from it, I've used:
this.SMN().Site , this does not throw errors but does not display any.
render() {
return (
</View>
<View>{this.SMN().Site}</View>
</View>
)
}
Make your Site as a component just like this:
const Site = () => {
return (
<View style={{ height: 400 }}>
<WebView
source={{ uri: 'https://www.google.com' }}
style={{ marginTop: 20 }} />
</View>
);
}
And in your render function use it like this:
render() {
return (
</View>
<View><Site /></View>
</View>
)
}
I think you want to get the return you want from SMN(). So this is what I thought your intended solution was.
SMN() {
return (
{
Site : () => (
<View style={{ height: 400 }}>
<WebView source={{ uri: 'https://www.google.com' }}
style={{marginTop:20 }} />
</View>
)
}
);
});
render() {
return (
</View>
<View>{this.SMN().Site}</View>
</View>
)
}

Rendering with map twice leads to error in React Native

<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/

How do I map over two arrays at the same time in react native?

How do I map over two different arrays in react native? In my case I'm fetching a response from server and mapping over it. Also there is another array named images which I want to list along with the fetched response from server.But the second mapping is looping within the first one. How do I separate it from the first?Following is my code.
sample code
<ScrollView>
{this.state.workers.map(a =>
<CardSection>
<TouchableOpacity onPress={() => this.popupDialog.show()}>
<View style={{ marginTop: 10, marginLeft:120}}>
{images.map(b =>
<Image
style={{ height: 100, width: 100 }}
source={{ uri: b.image }}
/>
)}
<Text style={{marginLeft:20, fontSize:20}}>{a.work_type}</Text>
</View>
</TouchableOpacity>
</CardSection>
)}
workers array is the json response I'm fetching from server.images array is as folows
export const images = [
{
image:'http://localhost:3000/Images/carpenter.png',
text:'hello'
},
{
image:'http://localhost:3000/Images/electrician.png',
text:'hii'
},
]
Also this how workers array looks like
updated
[
{
"sl.no": 1,
"worker_id": "wr_1",
"work_type": "carpenter",
"phone_num": "3456789243"
},
{
"sl.no": 2,
"worker_id": "wr_2",
"work_type": "electrician",
"phone_num": "345221344"
},
{
"sl.no": 3,
"worker_id": "wr_3",
"work_type": "plumber",
"phone_num": "8976545677"
}
]
You can simply move it above the first map and save the result:
render() {
const imagesToRender = images.map(b => {
return (
<Image
style={{ height: 100, width: 100 }}
source={{ uri: b.image }}
/>
);
});
return (
<ScrollView>
{this.state.workers.map(a =>
<CardSection>
<TouchableOpacity onPress={() => this.popupDialog.show()}>
<View style={{ marginTop: 10, marginLeft:120}}>
{imagesToRender}
<Text style={{marginLeft:20, fontSize:20}}>{a.work_type}</Text>
</View>
</TouchableOpacity>
</CardSection>
)}
</ScrollView>
);
}
Also, don't forget to add key props to each Image and each CardSection.
you can easily use flatlist with better performance
import React, { Component } from "react";
import { View, FlatList, TouchableOpacity, Image, Text } from "react-native";
const workers = [
{ id: 1, name: 'Nima', images: [{ image: 'https://www.lens-rumors.com/wp-content/uploads/2014/10/Nikon-AF-S-DX-Nikkor-18-140mm-f3.5-5.6G-ED-VR-sample-images1.jpg', text: 'hello' },{ image: 'https://www.lens-rumors.com/wp-content/uploads/2014/10/Nikon-AF-S-DX-Nikkor-18-140mm-f3.5-5.6G-ED-VR-sample-images1.jpg', text: 'hello' },{ image: 'https://www.lens-rumors.com/wp-content/uploads/2014/10/Nikon-AF-S-DX-Nikkor-18-140mm-f3.5-5.6G-ED-VR-sample-images1.jpg', text: 'hello' },{ image: 'https://www.lens-rumors.com/wp-content/uploads/2014/10/Nikon-AF-S-DX-Nikkor-18-140mm-f3.5-5.6G-ED-VR-sample-images1.jpg', text: 'hello' }] },
{ id: 2, name: 'Mike', images: [{ image: 'https://www.dike.lib.ia.us/images/sample-1.jpg/image', text: 'goodby' },{ image: 'https://www.dike.lib.ia.us/images/sample-1.jpg/image', text: 'goodby' },{ image: 'https://www.dike.lib.ia.us/images/sample-1.jpg/image', text: 'goodby' },{ image: 'https://www.dike.lib.ia.us/images/sample-1.jpg/image', text: 'goodby' },] },
]
class Test extends Component {
constructor(props) {
super(props);
this.state = {
workers: workers
};
}
_renderItem = ({ item }) => {
console.log(item);
return (
<View style={{ flex: 1 }}>
<TouchableOpacity>
<View style={{ marginTop: 10, marginLeft: 120 }}>
{item.images.map((b, index) => {
console.log(b.image);
return (
<View key={index}>
<Image
style={{ height: 100, width: 100 }}
source={{ uri: b.image }}
/>
<Text
style={{ marginLeft: 20, fontSize: 20, color: "black" }}
>
{b.text}
</Text>
</View>
);
})}
<Text style={{ marginLeft: 20, fontSize: 20, color: "black" }}>
{item.name}
</Text>
</View>
</TouchableOpacity>
</View>
);
};
_keyExtractor = (item, index) => item.id.toString();
render() {
return (
<FlatList
data={this.state.workers}
extraData={this.state}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
/>
);
}
}
export default Test;
If the images don't load, it's probably because you pass the wrong properties to the <Image /> component. Look up the docs for <Image /> component or replace it with <img /> instead and pass the url string of the image to the src attribute.
getImageUri(worker) {
// Decide which image to return for a certain type of worker
// For more workers and images, change the following logic
if(worker.work_type == 'carpenter') {
return images[0].image;
} else if(worker.work_type == 'electrician') {
return images[1].image;
} else {
return '';
}
}
render() {
...
<ScrollView>
{this.state.workers.map((a, index) =>
<CardSection>
<TouchableOpacity onPress={() => this.popupDialog.show()}>
<View style={{ marginTop: 10, marginLeft:120}}>
<Image
style={{ height: 100, width: 100 }}
source={{ uri: this.getImageUri(a)}}
/>
<Text style={{marginLeft:20, fontSize:20}}>{a.work_type}</Text>
</View>
</TouchableOpacity>
</CardSection>
)}
</ScrollView>
...
}

React Native appending Text component inside of another Text Component

I have a text component
<Text ref="text" style{...}>{this.state.te}</Text>
and I would like to insert and append other <Text></Text>
so when the event fires on a button I want it to insert a new <Text></Text> to look like this
<Text ref="text" style{...}>
<Text ref="text" style{...}>first</Text>
<Text ref="text" style{...}>second</Text>
<Text ref="text" style{...}>third</Text>
</Text>
this is what my event looks like
insertText(){
this.setState({te: this.state.te + <Text style={...}>{this.refs.newText.props.value}</Text>})
}
when I do this all it renders is "[object object]" inside of the Text Component
if I use
this.setState({te: <Text style={...}>{this.refs.newText.props.value}</Text>})
it will render the text just fine with a single <Text></Text> element.
any help would be appreciated. Thanks.
Edit:
this.state.te holds te: <Text></Text>
Ok, check out what I have below. I've essentially created an array of text values that are place in between two <Text> fields, and when the insertText function is called, it pushes a new text element to the array, then resets the state of the array to the te property:
getInitialState: function() {
return {
te: [<Text>Yo</Text>],
index:1
}
},
insertText: function() {
this.state.te.push(
<Text>Text number {this.state.index}</Text>
)
this.setState({
index: this.state.index + 1,
te: this.state.te
})
},
render: function() {
var MyText = function(t) {
return(
<Text>
{t}
</Text>
)
}
return (
<View style={styles.container}>
{MyText(this.state.te)}
<TouchableHighlight onPress={ () => this.insertText() } style={{ marginTop:20, height:60, flexDirection: 'row', backgroundColor: '#ededed', justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ fontSize: 22 }}>Add Text</Text>
</TouchableHighlight>
</View>
);
I've set up the full working project here. Full code is also posted below.
https://rnplay.org/apps/Itk6RQ
'use strict';
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
Text,
View,
TouchableHighlight
} = React;
var SampleApp = React.createClass({
getInitialState: function() {
return {
te: [<Text>Yo</Text>],
index:1
}
},
insertText: function() {
this.state.te.push(
<Text>Text number {this.state.index}</Text>
)
this.setState({
index: this.state.index + 1,
te: this.state.te
})
},
render: function() {
var MyText = function(t) {
return(
<Text>
{t}
</Text>
)
}
return (
<View style={styles.container}>
{MyText(this.state.te)}
<TouchableHighlight onPress={ () => this.insertText() } style={{ marginTop:20, height:60, flexDirection: 'row', backgroundColor: '#ededed', justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ fontSize: 22 }}>Add Text</Text>
</TouchableHighlight>
</View>
);
}
});
var styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 60
}
});
AppRegistry.registerComponent('SampleApp', () => SampleApp);

Categories

Resources