React app with axios inside useEffect() is rendering twice - axios

I've been trying to figure out why my function that draws a BPMN diagram is rendering the diagram twice. I have looked at various examples using axios inside useEffect but cannot figure out why it is happening. The url is returning a valid xml for the viewer.
Can anyone give me some guidance?
Here is the entire function
function RenderBPMN(pathDefinition) {
const [diagram, setDiagram] = useState("");
const container = document.getElementById("container");
const msg = JSON.stringify(pathDefinition);
const url = `http://localhost:9090/xml?path=${msg}`;
useEffect(() => {
const fetchData = async () => {
axios
.get(url)
.then((resp) => {
setDiagram(resp.data);
})
.catch ((error) => {
console.log(error);
});
};
fetchData();
}, [url]);
if (diagram.length > 0) {
const viewer = new Viewer({
container,
keyboard: {
bindTo: document
}
});
viewer
.importXML(diagram)
.then(({ warnings }) => {
if (warnings.length) {
console.log("Warnings", warnings);
}
viewer.get('canvas').zoom('fit-viewport');
})
.catch((err) => {
console.log("error", err.message);
});
}
return (
<div
id="container"
style={{
border: "1px solid #010101",
height: "50vh",
width: "70vw",
margin: "auto"
}}
></div>
);
}
export default RenderBPMN;

Remove url from useEffect array. With empty array it will by called only once when component will be mounted.
useEffect( () => {
// your code
}, [ /* empty */ ])

Related

Nexjs + SWR: API resolved without sending a response for /api/projects/<slug>, this may result in stalled requests

Since on first render I was not able to get the router.query I am passing the params from getServerSideProps as follows:
export async function getServerSideProps(context) {
return {
props: { params: context.params },
};
}
Then in the function am trying to do the API call but am getting the API stalled error
API resolved without sending a response for
/api/projects/nichole_robel23, this may result in stalled requests.
This is my code:
export default function Project({ params }) {
const { slug } = params;
let [projectData, setProjectData] = useState([]);
let [loading, setLoading] = useState(true);
const { data } = useSWR('http://localhost:3000/api/projects/' + slug);
useEffect(() => {
if (data) {
setProjectData(data.data.project);
setLoading(false);
}
}, [data]);
......
I have global SWRCofig as follows
<SWRConfig value={{ fetcher: (url) => axios(url).then(r => r.data) }}>
<Layout>
<Component {...pageProps} />
</Layout>
</SWRConfig>
Any way to solve the problem?
You are missing your fetcher–the function that accepts the key of SWR and returns the data, so the API is not being called.
You are also not returning a response correctly from the API–this is most likely a case of not waiting for a promise/async to be fulfilled correctly.
CLIENT
const fetcher = (...args) => fetch(...args).then((res) => res.json());
export default function Home({ params }) {
const { slug } = params;
const [projectData, setProjectData] = useState([]);
const [loading, setLoading] = useState(true);
const { data } = useSWR(`http://localhost:3000/api/projects/${slug}`, fetcher);
useEffect(() => {
if (data) {
setProjectData(data);
setLoading(false);
}
}, [data]);
API
const getData = () => {
return new Promise((resolve, reject) => {
// simulate delay
setTimeout(() => {
return resolve([{ name: 'luke' }, { name: 'darth' }]);
}, 2000);
});
}
export default async (req, res) => {
// below will result in: API resolved without sending a response for /api/projects/vader, this may result in stalled requests
// getData()
// .then((data) => {
// res.status(200).json(data);
// });
// better
const data = await getData();
res.status(200).json(data);
}

How to resolve Error " Too many re-renders. React limits the number of renders to prevent an infinite loop." in react native?

I have develop form to submit complaint by selecting product prom picker. So I externally implement the picker and then use it in complaint submission form. For that I was needed to take selectedValue state into complaint submission form by using function handle change. After i implement that faction and change appropriate places regarding to the tutorial link : [https://github.com/reactjs/reactjs.org/issues/1689][1]
But when I navigate to the complaint submission form it says the error as " Error: Too many re-renders. React limits the number of renders to prevent an infinite loop."So help me to resove and please confirm am I follow the above tutorial correctly.
Complaint submission form:
import * as React from 'react';
import {Button, View, Text, ScrollView, StyleSheet, Alert} from 'react-native';
import {Appbar} from 'react-native-paper';
import {TextInput, HelperText} from 'react-native-paper';
import {useEffect, useState} from 'react';
import AsyncStorage from '#react-native-community/async-storage';
import ProductPicker from './ProductPicker';
const ComplaintSubmission = ({navigation}) => {
const [productID , setproductID] = useState('');
const [description , setdescription] = useState('');
const [token, setToken] = useState('');
useEffect(() => {
saveToken();
}, []);
function handleChange(newValue){
setproductID(newValue);
}
const saveToken = async () => {
const token = await AsyncStorage.getItem('userToken');
console.log('token from storage', token);
setToken(token);
}
const send = () =>{
fetch("http://10.0.2.2:3000/customer/lodge-complaint", {
method: "post",
headers: {
'Content-Type': 'application/json',
'Authentication': `Bearer ${token}`
},
body: JSON.stringify({
description : description,
productID : value
})
})
}
const openAlert = () => {
Alert.alert(
"Complaint Successfully Submitted",
"We review it as soon as possible. Thank you for reaching for us!",
[{
text: "OK",
onPress : () => navigation.navigate("DashboardDrawer" ),
}]
);
}
return (
<ScrollView>
<Appbar.Header>
<Appbar.BackAction onPress={() => navigation.goBack()} />
<Appbar.Content title="Submit Complaint" />
<Appbar.Action icon="magnify" onPress={() => navigation.openDrawer()} />
</Appbar.Header>
<Text>Plese Fill the following</Text>
<View>
{/*{console.log('renderer token', token)}*/}
<ProductPicker value={productID} onValueChange = {handleChange()} />
<HelperText type="info">
Make sure select the correct Product
</HelperText>
</View>
<TextInput
style={styles.PIDstyle}
label="Description"
onChangeText = {(description) => setdescription(description)}
/>
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Text>This is submittion</Text>
<Button onPress={() => {send(); openAlert();}} title="Submit Complaint" />
</View>
</ScrollView>
);
};
export default ComplaintSubmission;
const styles = StyleSheet.create({
PIDstyle: {
marginTop: 30,
marginLeft: 10,
marginRight: 10,
},
});
And this is the picker component:
import React, {useEffect, useState} from 'react';
import {View, StyleSheet} from 'react-native';
import {Picker} from '#react-native-picker/picker';
import AsyncStorage from '#react-native-community/async-storage';
const ProductPicker = () => {
const [selectedValue, setSelectedValue] = useState('');
const [productDetails, setproductDetails] = useState([]);
console.log('product id---', selectedValue);
useEffect(() => {
getProductList();
}, []);
function handleChange(event){
props.onValueChange(event.target.value);
}
const getProductList = async () => {
const token = await AsyncStorage.getItem('userToken');
console.log(' function eka athule------', token);
fetch('http://10.0.2.2:3000/customer/get-all-products', {
method: 'post',
headers: {
'Content-Type': 'application/json',
'Authentication': `Bearer ${token}`,
},
})
.then((response) => response.json())
.then((json) => setproductDetails(json.data))
.catch((error) => console.error(error));
};
return (
<View style={styles.container}>
<Picker
selectedValue={selectedValue}
style={{height: 40, width: 150}}
onValueChange={(itemValue, itemIndex) => {
setSelectedValue(itemValue);
handleChange();
}}
>
{productDetails.map((item, index) => {
return (
<Picker.Item label={item.productName} value={props.item.productID} key={index}/>);
})}
</Picker>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 40,
alignItems: 'center',
},
});
export default ProductPicker;
Why don't you pass the itemValue to handleChange?
onValueChange={(itemValue, itemIndex) => {
setSelectedValue(itemValue);
handleChange(itemValue);
}}
Then change the function:
function handleChange(value){
props.onValueChange(value);
}

How can I render fetched information from a REST API in React Native?

I'm developing a screen which fetches Spotify playlists of a particular category, and prints the playlist information (and then allows the user to play them). I'm currently just trying to print the names of the playlists, but I'm getting an error. The playlists are being fetched just fine, as I can see in my console, though I'm struggling to render those details onto the screen.
This is my code:
import React, { Component } from 'react'
import { StyleSheet, SafeAreaView, ActivityIndicator, View, Text, Image} from 'react-native';
import * as Font from 'expo-font';
import styled from 'styled-components';
import { Ionicons } from '#expo/vector-icons';
import dimensions from '../components/ScreenSize';
import colours from '../components/Colours';
import { Audio } from 'expo-av';
import { FlatList, TouchableHighlight } from 'react-native-gesture-handler';
const client_id = {Client_ID}
const client_secret = {Client_Secret}
const item = ({item}) => (
<View>
<TouchableHighlight onPress={() => this.fetchTracks(item.id)}>
<View>
<Text {{uri:item.name}}/>
</View>
</TouchableHighlight>
</View>
)
export default class HomeScreen extends React.Component {
state={
fontsLoaded:false,
}
async componentDidMount() {
await Font.loadAsync({
'montserrat-regular': require('../assets/fonts/Montserrat/Montserrat-Regular.ttf'),
'montserrat-light': require('../assets/fonts/Montserrat/Montserrat-Light.ttf'),
'montserrat-semibold': require('../assets/fonts/Montserrat/Montserrat-SemiBold.ttf'),
'montserrat-bold': require('../assets/fonts/Montserrat/Montserrat-Bold.ttf'),
}
).then(() => this.setState({ fontsLoaded:true}))
this.getToken();
this.setAudio();
}
constructor (props) {
super(props)
this.playbackInstance=null;
this.state = {
playing:false,
token: '',
DATA:[],
};
}
setAudio=() => {
Audio.setAudioModeAsync({
allowsRecordingIOS:false,
interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX,
playsInSilentModeIOS: true,
shouldDuckAndroid: true,
interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX,
playThroughEarpieceAndroid: false,
});
}
componentDidCatch(error, info)
{
console.log(error, info.componentStack);
}
getToken = async() =>
{
try
{
const getspotifytoken = await fetch("https://accounts.spotify.com/api/token",
{
method:'POST',
body: `grant_type=client_credentials&client_id=${client_id}&client_secret=${client_secret}`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
const spotifytoken = await getspotifytoken.json();
this.setState({
token: spotifytoken.access_token
});
console.log(this.state.token);
}
catch(err)
{
console.log("Error fetching data ", err);
}
}
search = async () => {
try
{
const query = "mood"
console.log('Searching: ', query)
const spotifyApiCall = await fetch(`https://api.spotify.com/v1/browse/categories/${query}/playlists?`, {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${this.state.token}`,
"Content-Type":'application/json'
}
})
const spotify = await spotifyApiCall.json();
console.log("Items ", spotify);
this.setState({
DATA: spotify.playlists.items,
})
}
catch (err)
{
console.log("Error fetching data ", err);
}
}
fetchTracks = async (playlistId) => {
console.log('Playlist ', playlistId)
try
{
const getplaylist = await fetch(`https://api.spotify.com/v1.playlist/${playlistId}`,
{
method:'GET',
headers: {
Accept:"application/json",
Authorization:`Bearer ${this.state.token}`,
"Content-Type":"application/json"
}
});
const playlist = await getplaylist.json();
console.log('music ', playlist.tracks.items[0].preview_url);
}
catch (err)
{
console.log("Error fetching data ", err);
}
}
async _loadNewPlaybackInstance(playing, track) {
if(this.playbackInstance != null)
{
await this.playbackInstance.unloadAsync();
this.playbackInstance.setOnPlaybackStatusUpdate(null);
this.playbackInstance = null;
}
const source = {uri: track};
const initialStatus = {
shouldPlay: true,
rate: 1.0,
shouldCorrectPitch: true,
volume: 1.0,
isMuted: false
};
const {sound, status} = await Audio.Sound.createAsync(
source.initialStatus);
this.playbackInstance=sound;
this.playbackInstance.setIsLoopingAsync(false);
this.playbackInstance.playAsync();
if (this.state.selected === playlistId) {
console.log("Playing, so stop");
this.setState({selected:null});
this.playbackInstance.pauseAsync();
return;
}
this.setState({ selected:playlistId});
this._loadNewPlaybackInstance(true, playlist.tracks.items[0].preview_url);
}
render() {
if(!this.state.fontsLoaded ) {
return null
}
return (
<Container>
<Titlebar>
<Title>Music</Title>
</Titlebar>
<HeaderBar2>
<TouchableHighlight onPress={() => this.search()}>
<Header2>Playlists for your Mood</Header2>
</TouchableHighlight>
</HeaderBar2>
<View>
<FlatList
data = {this.state.DATA}
renderItem={item}
keyExtractor = {item.id}
numColumns={2}
extraData = {this.state}
/>
</View>
</Container>
);
}
}
And the error is: Unexpected token, expected '...' (18:23)
which is in reference to the line which reads <Text {{uri:item.name}}/>
I've also tried <Text {{item.name}}/>, as well as a few other alternatives, but I can't quite figure it out.
Thank you for reading, I'd really appreciate any help!
The thing you are doing wrong is you can not use Text like you are using above, check with this:
replace
<Text {{uri:item.name}}/>
to
<Text>{item.name}</Text>
Hope this helps!

Best practice for using React hooks and Context API to update global state and fetch/provide data from multiple endpoints

I am new to React hooks/Context API. I have read the React hook/context docs, and I am still having trouble with the following:
My attempts to update global state by multiple consumer components
currently causes frequent overwriting of context state due to
rerendering (e.g., activity or details state is sometimes
null/undefined). This probably is why...
... I am getting 400 (bad request) and/or 500 (server) errors on random refreshes of the page (~30% of the time content loads as
expected, ~70% errors are thrown. I believe this is happening
because we have various context states that are being called
asynchronously).
I am not sure how to implement Axios Cancellation, given that our useEffect hooks are calling dispatch functions (e.g.,
getActivities()) in different files. The examples I've seen
involve fetching data within the component (rather than in context).
I am seeking assistance for #1 specifically. I would love guidance on how to accurately fetch data and store in context as global state, and then provide that context to child components, allowing them to consume/update context state without unnecessary rerendering.
Tried to only provide relevant code snippets below:
ActivityState.js -- should fetch activity data
...
const ActivityState = props => {
const initialState = {
activities: [],
isLoading: false,
isError: false
};
const HEADERS = {
'Content-Type': 'application/json',
'user_id': 1
}
const [state, dispatch] = useReducer(ActivityReducer, initialState);
const userContext = useContext(UserContext);
const getActivities = async () => {
const { loggedIn } = contactContext;
let didCancel = false; // attempts to start implementing axios cancellation
try {
const res = await axios.get(url);
dispatch({ type: GET_ACTIVITIES, payload: res.data.data.activities });
} catch (err) {
if (!didCancel) {
dispatch({ type: 'FETCH_FAILURE' });
}
}
}
const updateActivity = (path, data) => { //update context state
dispatch({ type: UPDATE_ACTIVITY, payload: { path: path, data: data } });
};
const saveActivity = () => { //send new activity data to the backend
const postData = {
actions: [{"293939": []}],
activities: state.activities
};
try {
const res = axios.post(url,{ data: postData }, { headers: HEADERS });
} catch (err) {
console.log(err);
}
}
return (
<ActivityContext.Provider
value={{
activities: state.activities,
data: state.data,
backup_data: state.backup_data,
getActivities,
updateActivity,
saveActivity,
}}
>
{props.children}
</ActivityContext.Provider>
);
};
export default ActivityState;
ActivityReducer.js -- switch statements to be dispatched by ActivityState.js
...
export default (state, action) => {
switch (action.type) {
case GET_ACTIVITIES:
return {
...state,
activities: action.payload,
isLoading: true
};
case FETCH_FAILURE:
return {
...state,
isLoading: false,
isError: true
};
case UPDATE_ACTIVITY:
const { payload: { path }, payload } = action;
const data = state;
if (!data.activities)
return { data };
const index = data.activities.findIndex(e => e.socium_tracking_number == path.id);
if(index === -1)
return { data };
_.set(data, `activities[${index}].${path.field}`, payload.data);
return {
data,
};
...
DetailsState.js -- dispatch functions to fetch details
const DetailsState = props => {
const initialState = {
details: null,
};
const [state, dispatch] = useReducer(DetailsReducer, initialState);
const getDetails = async () => {
try {
const res = await axios.get(url);
dispatch({ type: GET_DETAILS, payload: res.data.data[0].details});
}catch(err) {
console.log(err)
}
};
return (
<DetailsContext.Provider
value={{ details: state.details, getDetails }}
>
{ props.children }
</DetailsContext.Provider>
);
}
export default SchemaState;
DetailsReducer.js -- switch statement
export default (state, action) => {
switch (action.type) {
case GET_DETAILS:
return {
...state,
details: action.payload,
};
default:
return state;
}
};
ActivityTable.js -- component that consumes Activity Info
...
const ActivityTable = ({ activity }) => {
const activityContext = useContext(ActivityContext);
const { activities, filtered, getActivities } = activityContext;
const [order, setOrder] = React.useState('asc');
const [orderBy, setOrderBy] = React.useState(activities.wait_time);
// Get activity data on mount
useEffect(() => {
async function fetchData() {
await getActivities()
}
fetchData();
}, []);
...
CreateActivity.js -- component that consumes Activity and Details data
...
const CreateActivity = props => {
const activityContext = useContext(ActivityContext);
const { activities, filtered, getActivities, addActivity } = activityContext;
const detailsContext = useContext(DetailsContext);
const { details, getDetails } = detailsContext;
// Get activity and details data on mount
useEffect(() => {
async function fetchData() {
await getActivities();
await getSchema();
}
fetchData();
}, []);
...
I really tried to get smarter on these issues before approaching the SO community, so that my question(s) was more defined. But this is what I have. Happy to provide any info that I missed or clarify confusion. Thank you for your time

fetching data from api and shows on Render Method

I am fetched data from api but the problem is
when i am show data in render method then it showing "Undefine"
Please Help me to fix it
This is my code:-
var ProductData=''
export default class ApiProduct extends Component {
FetchProduct=()=>{
fetch('https://drawtopic.in/projects/wordpress/wp- json/wc/v2/products?consumer_key=ck_044491712632ef889ec13c75daff5879a8291674&consumer_secret=cs_a8e16c732e1812017e15d278e1dce2765a88c49b',{
method:'GET',
})
.then((response) => response.json())
.then((res) =>{
ProductData= res;
})
}
render() {
{this.FetchProduct()}
{console.warn(ProductData)}
return (
<View/>
)}
i Want to Show All data in render method
Here is a quick Expo example that should show you how to render a simple list. It is not a good idea to call fetch inside the render method, as every re-render will call the fetch.
Here is an expo snack https://snack.expo.io/S1-LKIyQE
import React from 'react';
import { Text, View, StyleSheet, FlatList, SafeAreaView } from 'react-native';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
productData: []
}
}
async componentDidMount () {
await this.getData();
}
async getData() {
try {
let url ='https://drawtopic.in/projects/wordpress/wp-json/wc/v2/products?consumer_key=ck_044491712632ef889ec13c75daff5879a8291674&consumer_secret=cs_a8e16c732e1812017e15d278e1dce2765a88c49b'
let response = await fetch(url, { method:'GET' });
let responseJson = await response.json();
this.setState({productData: responseJson});
} catch (err) {
console.warn(err);
}
}
renderItem = ({item}) => {
return (
<View style={styles.mainItem}>
<Text>{item.name}</Text>
</View>
);
}
keyExtractor = (item, index) => {
return index.toString();
}
render() {
return (
<SafeAreaView style={styles.container}>
<FlatList
extraData={this.state}
data={this.state.productData}
keyExtractor={this.keyExtractor}
renderItem={this.renderItem}
/>
</SafeAreaView>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white'
},
mainItem: {
width:200,
height: 80,
justifyContent: 'center',
alignItems: 'center',
margin: 10,
backgroundColor: 'yellow',
borderColor: 'black',
borderWidth: 1
},
});
Here I have used async/await as it can make for much cleaner and clearer code. This is a great article on the differences between promises and async/await https://medium.com/#bluepnume/learn-about-promises-before-you-start-using-async-await-eb148164a9c8.
I have also given you a quick example on how to use a FlatList to display your data. You should check the docs on how to use it properly https://facebook.github.io/react-native/docs/flatlist
If you want to edit how each item is displayed on the screen then you need to update the renderItem method.
Try this way, if you have a question of how it works makes me to know.
let self;
export default class ApiProduct extends Component {
constructor(){
super();
self = this;
this.state = {
productData: null;
};
}
FetchProduct=()=>{
fetch('https://drawtopic.in/projects/wordpress/wp- json/wc/v2/products?consumer_key=ck_044491712632ef889ec13c75daff5879a8291674&consumer_secret=cs_a8e16c732e1812017e15d278e1dce2765a88c49b',{
method:'GET',
})
.then((response) => response.json())
.then((res) =>{
self.setState({ productData: res});
});
}
render() {
this.FetchProduct();
console.warn(self.state.productData);
return (
<View/>
);
}
I'll try to make order in your code.
Fetching data in the render method is not a good idea, it's better to use lifecycle methods, like componentDidMount. In order to handle your request result, set a state field and in your render read data from that field. So:
export default class ApiProduct extends Component {
constructor(){
super();
this.state = {
productData: undefined;
};
}
async componentDidMount(){
await this.fetchProduct();
}
fetchProduct = () => {
fetch('https://drawtopic.in/projects/wordpress/wp- json/wc/v2/products?consumer_key=ck_044491712632ef889ec13c75daff5879a8291674&consumer_secret=cs_a8e16c732e1812017e15d278e1dce2765a88c49b',{
method:'GET',
})
.then((response) => response.json())
.then((res) =>{
this.setState({
productData: res
})
})
}
render() {
const {productData} = this.state;
console.log(productData);
return (
<View/> // add here your code to render data properly
)
}}