How to change ref.current.children with useRef? - react-native

I wanted to change the content of the ref.current.children with ref.
Just like this:
import React, { useRef, useEffect } from 'react';
const MyApp = () => {
const aRef = useRef();
useEffect(()=> {
const node =aRef.current;
setTimeout(() => {
// Here I wanted to change the content of <Text> to 'Edited' but nothing happened.
node.children = React.Children.map(node.children, (child) => {
React.cloneElement(child, {}, 'Edited')
})
}, 1000)
}, [])
return (
<View ref={aRef}>
<Text>to be edited 1</Text>
<Text>to be edited 2</Text>
</View>
);
}
What I got:
to be edited 1
to be edited 2
What I wanted:
Edited
Edited
Is there a way to get what I wanted?

Related

Display all posts from database

I have a Firestore collection, schemed as follows:
posts{
uid{
userPosts{
postID{
creation:
postText:
}
}
}
}
I want to display all of the posts, so I've made the corresponding queries and saved them in posts - an array of all the posts that I later iterate through.
The problem with the way I do it is that it keeps adding the same posts every render. So I've tried to set the array each time, but that way the code never passes through these posts && posts.length > 0 condition.
I'm really new to RN and JS in general, but what I was expecting is
Nothing to show here
at first, and then the list of posts.
The complete component:
import { Text, Pressable, FlatList, SafeAreaView } from "react-native";
import { globalStyles } from "../../styles/global";
import React, { useState, useEffect } from "react";
import { db } from "../../../firebase";
import Post from "../../API/Post";
import { collection, getDocs } from "firebase/firestore";
const FeedScreen = ({ navigation }) => {
const [posts, setPosts] = useState([]);
useEffect(() => {
const getPostData = async () => {
setPosts([]); // ---> Without this line the posts keeps adding each render
const q = collection(db, "posts");
const docSnap = await getDocs(q);
docSnap.docs.map(async (item) => {
const tmp = collection(db, "posts", item.id, "userPosts");
const tmpSnap = await getDocs(tmp);
tmpSnap.docs.map(async (element) => {
setPosts((prev) => {
prev.push(element.data());
return prev;
});
});
});
};
getPostData().catch(console.error);
return;
}, []);
return (
<SafeAreaView style={globalStyles.global}>
{posts && posts.length > 0 ? (
<FlatList
data={posts}
renderItem={({ item }) => (
<Post
post={item}
navigation={navigation}
style={globalStyles.list_of_posts}
/>
)}
keyExtractor={(item, index) => index.toString()}
/>
) : (
<Text>Nothing to show here</Text>
)}
<Pressable
title="edit"
onPress={() => {
navigation.navigate("CreatePost", { navigation });
}}
style={globalStyles.plus_btn}
>
<Text style={globalStyles.plus_btn_text}>+</Text>
</Pressable>
</SafeAreaView>
);
};
export default FeedScreen;
As said, I'm new to this so I'd love an explanation of what actually happens and how to do it properly.
I think the prev value of setPosts will always be [] since it does not immediately update if you call it. A standard way to do it is to call setPosts at the end of your function. Can you try this one?
useEffect(() => {
const getPostData = async () => {
const q = collection(db, "posts");
const docSnap = await getDocs(q);
const promises = docSnap.docs.map(async (item) => {
const tmp = collection(db, "posts", item.id, "userPosts");
const tmpSnap = await getDocs(tmp);
return tmpSnap.docs.map((element) => element.data());
});
const arrayOfPosts = await Promise.all(promises);
let newPosts = [];
arrayOfPosts.forEach((posts) => {
newPosts = [...newPosts, ...posts];
});
setPosts(newPosts);
};
getPostData().catch(console.error);
return;
}, []);

dynamic textInput re-renders the whole component

It is part of a course so library is not an option. Basically, given a json object, generate a form. I can see the elements but I can't type in them. From my understanding, on each typing, the component is being rendered so the useState is being re-initalized. The only way I can type is if i remove the
value={formFields[id]}
from the TextInput.
https://snack.expo.io/#wusile/tenacious-fudge
Here is my code:
/* eslint-disable react/jsx-closing-bracket-location */
import React, { useContext, useState, useEffect } from 'react';
import { View, ScrollView, Text } from 'react-native';
import { TextInput, Button } from 'react-native-paper';
/*
Build a form dynamically from jsonData
see examples/sampleform.json file
*/
const defineFormFields = (data) => {
/*
Define the state to use along with default values.
*/
const stateData = {};
data.forEach((e) => {
stateData[e.id] = e.defaultValue || '';
});
return stateData;
};
const MyComponent = ({ jsonData }) => {
const [formFields, updateFormFields] = useState(defineFormFields(jsonData));
const [currentSegmentElements, updateCurrentViewElements] = useState([]);
const updateFormData = (fieldName, value) => {
const updatedValue = {};
updatedValue[fieldName] = value;
updateFormFields({
...formFields,
...updatedValue
});
};
const elementTypes = {
text(label, id) {
return (
<TextInput
key={id}
accessibilityHint={label}
label={label}
defaultValue={formFields[id]}
value={formFields[id]}
placeholder={label}
onChangeText={(value) => updateFormData(id, value)}
/>
);
}
};
const buildSegment = () => {
/*
Which segment/portion of the json to show
*/
const uiElements = [];
jsonData.forEach((e) => {
const definition = elementTypes[e.type](
e.label,
e.id
);
uiElements.push(definition);
});
updateCurrentViewElements(uiElements);
};
useEffect(() => {
buildSegment();
}, []);
return (
<ScrollView>
<View>
<View>
{currentSegmentElements.map((m) => m)}
</View>
</View>
</ScrollView>
);
};
const FormBuilder = React.memo(MyComponent);
export default FormBuilder;
Now where I need a form, I do:
const jsonData = [
{
"id":"FN",
"label":"FIrst Name",
"type":"text"
},
{
"id":"SN",
"label":"Last Name",
"type":"text"
},
{
"id":"countryCode",
"label":"Country Code",
"defaultValue":"US",
"type":"text"
}
]
<FormBuilder jsonData={jsonData} />
replace your useEffect by this.
useEffect(() => {
buildSegment();
}, [formFields]);

How to setting state with navigation params?

I am working on a React-native project with its basic packets(navigation etc). I have two screens. First there is a button and when i click the button. It's navigate to another screen which has flatlist. Then i click value in flatlist it is gives me a value . I can send that value to first screen with this.props.navigation.navigate and i can show it in console but i dont know how to use it to change buttonText which in my first screen? Where should i use setstate function in first screen ? (sorry for english)
Home.js
import React, {Component} from 'react';
import {View, Text} from 'react-native';
import {InputWithButton} from '../components/TextInput';
//const TEMP_BASE_CURRENCY = 'USD';
//const TEMP_CONVERT_CURRENCY = 'GBP';
class Home extends Component {
constructor(props) {
super(props);
this.state = {
baseCurrency: 'TRY', //Başlangıç olarak sayfa açıldığında gelecek olan değerler
convertCurrency: 'USD',
amount: null,
result: '',
date: '',
};
//const selected = this.props.route.params;
}
calculate = () => {
const amount = this.state.amount;
let url =
'https://api.exchangeratesapi.io/latest?base=' + this.state.baseCurrency;
fetch(url, {
method: 'GET',
})
.then((res) => res.json())
.then((data) => {
const date = data.date;
const result = (
data.rates[this.state.convertCurrency] * amount
).toFixed(2);
this.setState({
result,
date,
});
})
.catch((error) => {
console.log(error);
});
};
handleChangeText = (text) => {
//Yazıda değişim algılandığında api işlemleri başlasın
this.setState(
{
amount: text,
},
this.calculate,
);
};
handlePressBaseCurrency = () => {
//flatlist sayfası açılsın
const {navigation} = this.props;
navigation.navigate('CurrencyList');
};
handlePressConvertCurrency = () => {
//flatlist sayfası açılsın
};
render() {
const {baseCurrency, convertCurrency, amount, result, date} = this.state;
return (
<View>
<InputWithButton
buttonText={baseCurrency}
onPress={this.handlePressBaseCurrency}
keyboardType="numeric"
onChangeText={(text) => this.handleChangeText(text)}
/>
<InputWithButton
editable={false}
buttonText={convertCurrency}
onPress={this.handlePressConvertCurrency}
value={result}
/>
</View>
);
}
}
export default Home;
CurrencyList.js
import React, {Component} from 'react';
import {View, FlatList, Text} from 'react-native';
import currencies from '../data/currencies';
import {ListItem, Separator} from '../components/List';
const temp_base_currency = 'CAD';
class CurrencyList extends Component {
constructor(props) {
super(props);
this.state = {
selectedItem: '',
};
}
handlePress = (item) => {
this.setState({
selectedItem: item, //__
});
// const {navigate} = this.props.navigation;
// navigate('Home', {clickedItem: this.state.selectedItem});
//Tıklandığında beklesin
setTimeout(
() => this.props.navigation.navigate('Home', {selected: item}),
1,
); //__
};
render() {
return (
<View>
<FlatList
renderItem={({item}) => (
<ListItem
onPress={() => this.handlePress(item)}
text={item}
selected={item === this.state.selectedItem} //__
/>
)}
data={currencies}
keyExtractor={(item) => item}
ItemSeparatorComponent={Separator}
/>
</View>
);
}
}
export default CurrencyList;
It would have been better if you shared your code but here is what I would do.
SECOND SCREEN
this.props.navigation.navigate('firstScreen', {
name: 'Your value'
})
FIRST SCREEN
const name = this.props.route.params.name;
<Button>{name}</Button
You can pass the selected item from the Flatlist to the Home screen like this:
Home.js:
this.props.navigation.navigate('CurrencyList',
{
onGoback: (item) => this.setState({})
})
CurrencyList.js:
handlePress: (item) => {
/** your code **/
this.props.navigation.state.params.onGoBack(item)
this.props.navigation.navigate('Home')
}

Tour view does not have React internal Fiber

I want implementation app-tour in my project , I installed react-native-app-tour .I have a View that I want assign app-tour to it and I use ref in view that it dispatch AppTourView to inputreducer and finally in useEffect I run appTour but when I run app I encountered Error that error is Tour view does not have React internal Fiber, please can help me
import React, {
useState,
useEffect,
useCallback,
useReducer,
useRef,
} from 'react';
import {AppTour, AppTourSequence, AppTourView} from 'react-native-app-tour';
const inputReducer = (state, action) => {
if (action.type == 'SET_APPTOUR') {
return [...state.appTour, action.data];
}
};
export default function HomeScreen(props) {
const [forsat, setForSat] = useState(null);
const [appTourTargets, dispatchAppTour] = useReducer(inputReducer, {
appTour: [],
});
useEffect(() => {
setTimeout(() => {
console.log(appTourTargets);
let appTourSequence = new AppTourSequence();
appTourTargets.appTour.forEach(appTourTarget => {
appTourSequence.add(appTourTarget);
});
AppTour.ShowSequence(appTourSequence);
}, 1000);
}, [appTourTargets]);
return(
<view style={styles.container}>
<View
ref={useCallback(ref => {
if (!ref) return;
setForSat(ref);
let prop = {
order: 12,
title: 'This is a target View',
description: 'We have the best targets, believe me',
outerCircleColor: '#3f52ae',
cancelable: false,
};
dispatchAppTour({type:'SET_APPTOUR', data:AppTourView.for(ref, {...prop})});
}, [])}
style={styles.rankContainer}>
<BText font="_Light" size={15} color={color.firstColor}>
فرصت{' '}
<BText font="_Bold" color={color.secondColor} size={15}>
{FormatHelper.toPersianString(daysToGo.toString())}
</BText>{' '}
روز تا کنکور
</BText>
</View>
</view>
)
}

How do I take a screenshot of View in React Native?

I want to share screenshot of particular View Component instead of whole screen.
Any one help me out with this.
Take a look a picture. Want screenshot of Red mark area which is within View Component.
You can use library named react-native-view-shot
You just have to give wrap your View inside ViewShot, take a reference of that and call capture()
Here is example of code taken from that library
import ViewShot from "react-native-view-shot";
class ExampleCaptureOnMountManually extends Component {
componentDidMount () {
this.refs.viewShot.capture().then(uri => {
console.log("do something with ", uri);
});
}
render() {
return (
<ViewShot ref="viewShot" options={{ format: "jpg", quality: 0.9 }}>
<Text>...Something to rasterize...</Text>
</ViewShot>
);
}
}
Here is a working example example of code using react-native-view-shot with hooks
import React, { useState, useRef, useEffect } from "react";
import { View, Image, ScrollView, TouchableOpacity } from "react-native";
import ViewShot from "react-native-view-shot";
var RNFS = require("react-native-fs");
import Share from "react-native-share";
const TransactionReceipt = () => {
const viewShotRef = useRef(null);
const [isSharingView, setSharingView] = useState(false);
useEffect(() => {
if (isSharingView) {
const shareScreenshot = async () => {
try {
const uri = await viewShotRef.current.capture();
const res = await RNFS.readFile(uri, "base64");
const urlString = `data:image/jpeg;base64,${res}`;
const info = '...';
const filename = '...';
const options = {
title: info,
message: info,
url: urlString,
type: "image/jpeg",
filename: filename,
subject: info,
};
await Share.open(options);
setSharingView(false);
} catch (error) {
setSharingView(false);
console.log("shareScreenshot error:", error);
}
};
shareScreenshot();
}
}, [isSharingView]);
return (
<ViewShot ref={viewShotRef} options={{ format: "jpg", quality: 0.9 }}>
<View>
{!isSharingView && (
<TouchableOpacity onPress={() => setSharingView(true)}>
<Image source={Images.shareIcon} />
</TouchableOpacity>
)}
<ScrollView />
</View>
</ViewShot>);
}