How to get data from function component? - react-native

I have a function component (A) import another function component (B).
B has a onChange value, I want to get the onChange value all the time when I import B in A.
I have no idea how to get it.
Here is the code like this:
A:
import B from './B';
const A = () => {
// I want to get B's onChange value over here
return (
<B />
);
}
B:
import React, { useState } from 'react';
const B = () => {
const [value, setValue] = useState('');
return (
<SomePicker value={value} onChange={(value) => setValue(value)}
);
}

You can refer below solution.
import B from './B';
const A = () => {
const getOnChangeValueFromB = (data) => {
console.log(data)// you will get onChange value here
};
// I want to get B's onChange value over here
return (
<B callBack={getOnChangeValueFromB}/>
);
}
import React, { useState } from 'react';
const B = (props) => {
const [value, setValue] = useState('');
const returnValueToA = (value) => {
setValue(value)
props.callBack(value);
}
return (
<SomePicker value={value} onChange={(value) => returnValueToA(value)}
);
}

Related

How to export data from files react-native

I have a few files like Train.js and Result.js.
How can i make export voiceData from Train.js to Result.js? Files bellow:
Train.js
const Train = () => {
const [user] = useAuth()
let [started, setStarted] = useState(false);
let [results, setResults] = useState([]);
const [voiceData, setVoiceData] = useState([]);
const navigation = useNavigation()
const toResult = () => {
navigation.navigate('Result')
}
Result.js:
return (
<View style={styles.container}>
<Text>{voiceData}</Text>
<View>
Depends on how you are using it and what you want to do with it but here's an example of exporting and importing.
// Train.js
export default Train = () => {
const voiceData = "hesdsdsdsdllo";
return voiceData;
}
// App.js
import React from 'react';
import Train from './Train.js';
export function App(props) {
return (
<div className='App'>
<h1>Hello React. {Train()}</h1>
</div>
);
}
Train is a function so return what you need from it.

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]);

Is there a way to test code that makes API calls without mocking?

I have a FlatList that implements endless scrolling. First it fetches data from a third-party API (Marvel). Then when user scrolls to the end it fetches more. I'm having issues with a duplicate ID in the Flatlist so want to add tests to check for this. However, Jest forces you to mock API calls. Is there a way to do the test without mocking?
I'm trying to avoid an E2E test framework if possible or is that the only way?
EDIT: Code below if it's relevant:
import React, {useEffect, useRef} from 'react';
import {CText} from '#components/CText';
import {fetchComics} from '#apis/marvelApi';
import {useState} from 'react';
import {Comic, ComicsResponse} from '#src/types/marvel';
import {CActivityIndicator} from '#components/CActivityIndicator';
import {FlatList} from 'react-native-gesture-handler';
import {View} from 'react-native';
import {ActivityIndicatorType} from '#src/types';
import styles from './styles';
const renderItem = ({item, index}: {item: Comic; index: number}) => {
return <CText type="paragraph">{item.title}</CText>;
};
const keyExtractor = (item: Comic) => {
return item.id.toString();
};
/**
* https://developer.marvel.com/docs#!/public/getComicsCharacterCollection_get_2
* Will get 409 error if Limit greater than 100
*/
const ITEMS_PER_PAGE = 100;
interface Props {
characterId: number;
}
const initialState = {
offset: 0,
totalResults: -1,
};
/**
* For some reason this component doesn't unmount if you leave the screen
* by pressing the back button
*/
const ComicsComponent = (props: Props) => {
const [comics, setComics] = useState<Comic[]>(undefined);
const [isBusy, setIsBusy] = useState(true);
const [activityIndicatortype, setActivityIndicatorType] = useState<
ActivityIndicatorType
>('absolute');
const state = useRef(initialState);
const characterId = props.characterId;
/**
* This effect gets called on load and should always have offset 0
*/
useEffect(() => {
state.current = initialState;
setActivityIndicatorType('absolute');
setIsBusy(true);
setComics(undefined);
const offset = 0;
fetchComics(characterId, offset, ITEMS_PER_PAGE).then(
(response: ComicsResponse) => {
setIsBusy(false);
setComics(response.data.results);
setActivityIndicatorType('small');
state.current.totalResults = response.data.total;
},
);
return () => {
console.log('Component unmounted');
};
}, [props.characterId]);
/**
* This function gets called when the user scrolls to end of FlatList
*/
const endReachedHandler = () => {
if (
state.current.totalResults > 0 &&
state.current.offset < state.current.totalResults
) {
if (isBusy) {
return;
}
const newOffset = state.current.offset + ITEMS_PER_PAGE;
state.current.offset = newOffset;
setIsBusy(true);
fetchComics(characterId, newOffset, ITEMS_PER_PAGE).then(
(response: ComicsResponse) => {
setIsBusy(false);
if (newOffset > response.data.total) {
return;
}
//do nothing since we reached the end
else {
console.log(`Offset: ${newOffset} Comics length:${comics.length}`);
const newComics = [...comics, ...response.data.results];
setComics(newComics);
}
},
);
}
};
const content = () => {
if (comics) {
return (
<FlatList
data={comics}
renderItem={renderItem}
keyExtractor={keyExtractor}
onEndReachedThreshold={2}
onEndReached={endReachedHandler}
/>
);
} else {
return null;
}
};
return (
<View style={styles.flex}>
{content()}
{isBusy && <CActivityIndicator type={activityIndicatortype} />}
</View>
);
};
export {ComicsComponent};
It turns out you can do API calls in Jest tests in react-native by using axios package.
import 'react-native';
import axios from 'axios';
const TIMEOUT = 5000;
beforeEach(() => {});
it(
'can call Axios',
async () => {
const result = await axios.get('https://api.scryfall.com/sets');
expect(result.status).toEqual(200);
expect(result.data.object).toEqual('list');
},
TIMEOUT,
);
I had troubles doing this with the plain fetch package.

React Native Date Range

import React, {useState} from "react";
import { StyleSheet, View, Text } from "react-native";
import { globalStyles } from "../styles/global";
import {Calendar, CalendarList, Agenda} from 'react-native-calendars';
import {LocaleConfig} from 'react-native-calendars';
import moment from "moment";
import DateRangePicker from "react-native-daterange-picker";
export default function About(){
const [endDate, setendDate] = useState(null)
const [startDate, setstartDate] = useState(null)
const [displayedDate, setdisplayedDate] = useState(moment())
state = {
endDate: null,
startDate: null,
displayedDate: moment()
};
const handleSubmit = (props) => {
console.log(props);
setendDate(props.endDate);
setstartDate(props.startDate);
setdisplayedDate(props.displayedDate);
// console.log(props.startDate);
// console.log(props.displayedDate);
}
return(
<View style={globalStyles.container}>
<DateRangePicker
onChange={ handleSubmit }
endDate={endDate}
startDate={startDate}
displayedDate={displayedDate}
range>
<Text>Click me!</Text>
</DateRangePicker>
</View>
)
}
1.not able to select date range.
2. undefined is not an object (evaluating displayedDate.format)
3. Using function component but most of the solutions are available with class component
You can call handleSubmit as follows
onChang={() => handleSubmit()}
And props param is needless in function prototype
const handleSubmit = (props) => {}
Because props is already declared and you don't need to set it as parameter.
If you want to use it as parameter then you should change like this
onChange={() => handleSubmit(props)}
Hope this helps you.
You can change your handleSubmit() function like this
const handleSubmit = (props) => {
if (props.startDate != undefined) {
setStartDate(props.startDate);
}
if (props.displayedDate != undefined) {
setDisplayedDate(props.displayedDate);
}
if (props.endDate != undefined) {
setEndDate(props.endDate);
}
}
Source: issues#15

useEffect returns unhandled promise

I have been for several hours trying to get an API to be called in ReactNative useEffect hook. Sometimes when I restart my app the value is resolved. But most of the time, I have an Unhandled promise rejection. I googled and tried various methods. I tried using .then etc.. I just can't figure it out.
import React, { useState, useContext, useEffect } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, FlatList } from 'react-native';
import { EvilIcons } from '#expo/vector-icons';
import jsonServer from '../api/jsonServer';
const ShowScreen = ({ navigation }) => {
const id = navigation.getParam('id');
const [post, setPost] = useState([]);
const getBlog = async () => {
const result = await jsonServer.get(`http://0.0.0.0/blog/docroot/jsonapi/node/article/${id}`);
return result;
}
useEffect(() => {
async function setToState() {
const val = await getBlog();
setPost(val);
}
setToState();
},[]);
return (
<View>
<Text>Here { console.log(post) }</Text>
</View>
);
};
ShowScreen.navigationOptions = ({ navigation }) => {
return {
headerRight: (
<TouchableOpacity
onPress={() =>
navigation.navigate('Edit', { id: navigation.getParam('id')
})}
>
<EvilIcons name="pencil" size={35} />
</TouchableOpacity>
)
};
};
const styles = StyleSheet.create({});
export default ShowScreen;
What you could do is something like this:
....
....
const [post, setPost] = useState([]);
const [isMounted, setIsMounted] = useState(false);
const getBlog = async () => {
const result = await jsonServer.get(`http://0.0.0.0/blog/docroot/jsonapi/node/article/${id}`);
return result;
}
useEffect(() => {
setIsMounted(true)
async function setToState() {
// using try catch I'm handling any type of rejection from promises. All errors will move to catch block.
try{
const val = await getBlog();
// checking if component is still mounted. If mounted then setting a value. We shouldn't update state on an unmounted component.
if(isMounted){
setPost(val);
}
} catch(err){
console.log("Error", err)
}
}
setToState();
return () => {
// Setting is mounted to false as the component is unmounted.
setIsMounted(false)
}
},[]);
I believe this will solve your Unhandled promise rejection error. Please try if it still doesn't solve the issue will create the same in Sanck.
I think my issue was not just promise, the issue is also seems to be me not handling undefined/null in the state. The below code is working for me.
import React, { useState, useContext, useEffect } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, FlatList } from 'react-native';
import { EvilIcons } from '#expo/vector-icons';
import jsonServer from '../api/jsonServer';
const ShowScreen = ({ navigation }) => {
const id = navigation.getParam('id');
const [post, setPost] = useState([]);
const getBlog = async () => {
const result = await jsonServer.get(`http://hello.com/jsonapi/node/article/${id}`).then(
res => {
setPost(res)
return res;
}, err => {
console.log(err);
});
}
useEffect(() => {
setPost(getBlog());
},[]);
return (
<View>
<Text>{ post.data ? post.data.data.id : "" }</Text>
</View>
);
};
export default ShowScreen;
Note: I am setting the state in useEffect as well as in the request. I am yet to check if I can just do it once.