useEffect React for function checkSession - reactjs

At first it worked, but then it stopped.The problem is that the same action is performed on the server 4 times, apparently this is due to useEffect
My component:
function TableComp() {
const session = useCheckCookieToken()
return (
session !== null ?
<>
<Menu />
<AddService />
<DataTable />
</>
:
<Navigate to="/" />
)
}
My service function:
async function useCheckCookieToken() {
const [session, setSession] = useState()
useEffect(() => {
async function sessionPost() {
const res = await fetch(`/api/checkCookie`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
})
if (res.ok) {
const result = await res.json()
setSession(result)
}
}
sessionPost()
return () => { console.log('unmount') }
}, [])
return session
}
module.exports = {
useCheckCookieToken
}
My API:
I tracked that in the interval highlighted in the screenshot from 97 to 101 lines, the same action is triggered 4 times, apparently the problem is related to the problem of mounting

Related

json response from mock server not printing but is in the console

I am trying to learn react, and I am making a successful API call, but it only prints in the console. I found examples but many of them recommended to use setData(json) but I am not able to use it because the file is a list of export async function which was also recommended.
export async function GetHellWorld() {
return fetch(`http://localhost:8080/api`, {
method: "Get",
headers: {
"Content-type": "application/json; charset=UTF-8"
}
}).then(response => response.json())
.then(json => {
console.log(json)
})
.catch(error => (console.log(error)))
}
and the component
function Test(thisArg, argArray) {
const result = GetHellWorld.apply()
return (
<div className="App">
{JSON.stringify(result)}
</div>
);
}
export default Test;
In the console I see "Hello World" but in the browser is get just {}.
Two questions:
How can I bind the JSON response to an object so I can do something like result.name.
Is this the correct was to call the await function? const result = GetHellWorld.apply()
---- update ----
I decided to try axios because I want to make multiple calls in one file.
const axios = require('axios');
export class AppService {
public async GetHelloWorld(): Promise<any> {
const response = await axios.get(`http://localhost:8080/api`, {
method: "Get",
headers: {
"Content-type": "application/json; charset=UTF-8"
}
}).catch(() => console.log("Issue in GetHelloWorld"))
return response.data
}
}
component
import React from 'react';
import {AppService} from "../services/app.service";
function Movies() {
const api = new AppService()
const hello = async () => {
const response = await api.GetHelloWorld();
console.log("The response: " + response)
}
return (
<div className="App">
{JSON.stringify(hello)}
</div>
);
}
note I had to add typescript support.
For whatever reason I get
Module not found: Error: Can't resolve '../services/app.service' in '/Users/miketye/programming/test-react/src/components'
While the other answer about using a custom hook can work, I would not recommend it while you're still leaning React.
Look up how to use the "useEffect" hook, that's generally how you want to do any sort of loading logic in React.
First off, you need to fix your async function so it actually returns a value:
// style/convention note, but non-component functions should not start with a capital letter
export async function getHelloWorld() {
return fetch(`http://localhost:8080/api`, {
method: "Get",
headers: {
"Content-type": "application/json; charset=UTF-8"
}
}).then(response => response.json())
.then(json => {
return json // will cause this function to return a Promise of type "string", since we're in an async function
})
// better to just let the error get thrown here, for testing
}
Then use it like this:
function Test(thisArg, argArray) {
[fetchResult, setFetchResult] = useState(undefined) // look up useState. State is how you have values that change over time in a resct component
useEffect(() => {
async function fetchData() {
const data = await getHelloWorld()
setFetchResult(data)
}
fetchData()
}, [])
// look up useEffect. Since the second argument (the "dependency array") is empty, useEffect will fire only once, after the component loads
return (
<div className="App">
{result ? JSON.stringify(result) : "no result yet"}
</div>
);
}
export default Test;
You can use a custom hook for this purpose:
import { useState } from "react";
const useFetchData = () => {
const [data, setData] = useState(null);
const fetchData = () => {
fetch("http://localhost:8080/api", {
method: "Get",
headers: {
"Content-type": "application/json; charset=UTF-8"
}
}).then(response => response.json())
.then(json => { setData(json); })
.catch(error => { console.log(error); })
}
useEffect(() => {
fetchData();
}, []);
return { data, fetchData };
}
export default useFetchData;
And then call it in your component:
import useFetchData from "#/hooks/useFetchData";
const Test = () => {
const { data, fetchData } = useFetchData();
// CALL fetchData IF YOU WANT TO UPDATE THE CURRENT STATE
return (
<div className="App">
{data && JSON.stringify(data)}
</div>
);
}
export default Test;

How to fetch api via looped callbacks with React functional components

So I have a 40+ loop that's calling another component to display images. Each image has an ID and with that ID I can get more information about the image like Name and description via another API call.
When DisplayImage gets called I want it to call another callback function that will send out API calls for that image's metadata, store it in a variable and display it as an H1 tag.
return (
<div>
{array.map(index) => {
// Some Other Code That return a TokenID //
<>
{displayImage(tokenId)}
</>
</div>
})
const displayImage = (tokenId) => {
const imageName = GetURI(tokenId)
return (
<div className="token-container">
<h1>{imageName}</h1>
<img className="artwork" width="250px" src={`https://ipfs-asdf/${tokenId}`} />
</div>
)
}
const GetURI = async (tokenId) => {
const res = await fetch("https://api"+tokenId , {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
}).then(data => {
console.log(data)
return data.json();
})
.then(data => {
return (data.name || [])
})
.catch(err => {
console.log(err);
});
}
The data is being displayed on the console but now I'm running into an infinite loop issue that I know UseEffect can solve but I can't quite figure it out. I managed to display the data on the console with UseEffect using the [] attribute but don't know how to display the data. Any help would be amazing. Thank you!
Two things useful to your situation
functions declared outside the component aren't recreated each render
useState and useEffect pairing limits calls to API to only when tokenId changes
// Put this function outside the component
// so it does not need a useCallback
// i.e not reconstructed each render of DisplayImage
const GetURI = async (tokenId) => {
...
});
const DisplayImage = (tokenId) => {
const [imageName, setImageName] = useState()
// limit calls to API to when tokenId changes
// and if eslint complains add GetURI to dependency list
// - but GetURI never changes, so no un-needed calls from it
useEffect(() => {
setImageName(GetURI(tokenId))
}, [tokenId, GetURI])
return (
<div className="token-container">
<h2>{imageName}</h2>
<img className="artwork" width="250px" src={`https://ipfs-asdf/${tokenId}`} />
</div>
)
};
You can also abstract to custom hook useImageName()
const GetURI = async (tokenId) => {
...
});
const useImageName = (tokenId) => {
const [imageName, setImageName] = useState()
useEffect(() => {
setImageName(GetURI(tokenId))
}, [tokenId, GetURI])
return imageName
})
const DisplayImage = (tokenId) => {
const imageName = useImageName(tokenId)
return (
<div className="token-container">
<h2>{imageName}</h2>
<img className="artwork" width="250px" src={`https://ipfs-asdf/${tokenId}`} />
</div>
)
};
BTW in GetURI this
return (data.name || [])
looks like should be
return data.name || ''
Is a different approach ok? I'd put display image into its own component.
const DisplayImage = ({tokenId: {_tokenId}}) => {
const imageName = GetURI(_tokenId)
const GetURI = useCallback(async () => {
await fetch("https://api"+tokenId , {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
}).then(data => {
console.log(data)
return data.json();
})
.then(data => {
return (data.name || [])
})
.catch(err => {
console.log(err);
});
})
});
useEffect(() => {
if (_tokenId) GetURI();
}, [GetURI]);
return (
<div className="token-container">
<h2>{imageName}</h2>
<img className="artwork" width="250px" src={`https://ipfs-asdf/${_tokenId}`} />
</div>
)
};
and then
return (
<div>
{array.map(index) => {
//Some Other Code//
<DisplayImage tokenId={tokenId} />
</div>
})
You should probably cache the response from GetURI(tokenId). No need to ask twice for the same URI when using the same tokenId.
An easy way is using react-query:
Setup in App.js:
// App.js
import { QueryClient, QueryClientProvider } from 'react-query'
const queryClient = new QueryClient()
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Example />
</QueryClientProvider>
)
}
Then use in a DisplayImage component (instead of inline function):
// DisplayImage.js
import { useQuery } from 'react-query'
export function DisplayImage(tokenId) {
const { isLoading, error, data: imageName } = useQuery(['images', tokenId], GetURI(tokenId))
return (
<div className="token-container">
<h1>{isLoading ? 'loading...' : imageName}</h1>
<img className="artwork" width="250px" src={`https://ipfs-asdf/${tokenId}`} />
</div>
)
}
I found the best way to go about it with everyones help on here so thanks!
I put the GetURI function inside the show image component, and had a useEffect method call GetURI every time there was a new token ID, then I set a state variable to whatever was returned.
No loops, no errors 👌
const DisplayImage = (data) => {
const [nftMetadata, setNftMetadata] = useState();
const GetURI = async (data) => {
const nftURI = await data.drizzle.contracts.Contract.methods.tokenURI(data.tokenId).call()
await fetch(nftURI , {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
"Access-Control-Allow-Origin": "*"
},
})
.then(data => {
return data.json();
})
.then(data => {
return setNftMetadata(data || []);
})
.catch(err => {
return console.log(err);
});
});
useEffect(() => {
GetURI(data);
}, [data.tokenId])
return (
<div className="token-container">
<h2>{nftMetadata.name}</h2>
<img className="artwork" width="450px" src={`https://ipfs:/whatever/${nftMetadata.image}`} />
</div>
);
};
return (
<div>
{array.map(index) => {
// Some Other Code That returns a TokenID //
<>
<DisplayImage address={drizzle.contractList[0].address} tokenId={tokenId} drizzle={drizzle} drizzleState={drizzleState}/>
</>
</div>
})

React project- chatting app. Need advice on setting interval

below is my main code for my project, which is a simple open chatting room.
This is a chatting client basically, which can only get chatting logs and post chats.
To log in, I request for an authorization key to the server and use that key to start chatting.
The thing that I want to do is to get the chatting logs from the server every 3 seconds.
So, after it retrieves chats every 3 seconds, the page should refresh with new chats.
However, this seems not to be working well for me.
If there is a good React pro who can solve this problem for me, please do.
import React from 'react';
import { withRouter } from 'react-router-dom';
import './Chat.css';
import InfoBar from '../InfoBar/InfoBar';
import Input from '../Input/Input';
import Messages from '../Messages/Messages';
const API_ENDPOINT = 'https://snu-web-random-chat.herokuapp.com';
let INTERVAL_ID;
const Chat = ({ token, setToken }) => {
const [ name, setName ] = React.useState('');
const [ message, setMessage ] = React.useState('');
const [ messages, setMessages ] = React.useState([]);
const [ lastEl, setLastEl ] = React.useState({});
React.useEffect(() => {
if (localStorage.getItem('username')) {
setName(localStorage.getItem('username'));
}
window.addEventListener('scroll', listenToScroll, true);
fetchData();
INTERVAL_ID = setInterval(() => {
fetchData();
}, 3000);
return () => {
window.removeEventListener('scroll', listenToScroll);
clearInterval(INTERVAL_ID);
};
}, []);
const fetchData = () => {
fetch(`${API_ENDPOINT}/chats?createdAtFrom=${lastEl.createdAt || ''}`)
.then((res) => res.json())
.then((msgs) => {
const updatedMsgs = messages.concat(msgs);
setLastEl(msgs[msgs.length - 1]);
setMessages(updatedMsgs);
});
};
const listenToScroll = (e) => {
const div = document.getElementById('messagesContainer');
// console.log(window.scrollY);
// const winScroll = document.body.scrollTop || document.documentElement.scrollTop;
// const height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
// const scrolled = winScroll / height;
};
const onLogin = (e) => {
e.preventDefault();
fetch(`${API_ENDPOINT}/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: `name=${name}`
})
.then((response) => response.json())
.then(({ key }) => {
if (key) {
setToken(true);
localStorage.setItem('__key', key);
localStorage.setItem('username', name);
}
})
.catch((err) => console.error(err));
};
const sendMessage = (e) => {
e.preventDefault();
if (message) {
fetch(`${API_ENDPOINT}/chats`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Key ${localStorage.getItem('__key')}`
},
body: `message=${message}`
})
.then((response) => response.json())
.then((res) => {
const obj = {
createdAt: res.createdAt,
message: res.message,
userName: res.user.name,
_id: res.user._id
};
setMessages([ ...messages, obj ]);
})
.catch((err) => console.error(err));
setMessage('');
}
};
const logout = () => {
localStorage.removeItem('__key');
localStorage.removeItem('username');
setToken(false);
};
const loadMessages = () => {
fetchData();
clearInterval(INTERVAL_ID);
};
return (
<div className="outerContainer">
<div className="innerContainer">
<InfoBar
name={name}
token={token}
handleInput={(e) => setName(e.target.value)}
handleLogin={onLogin}
handleLogout={logout}
/>
<Messages messages={messages} name={name} lastEl={lastEl} loadMore={loadMessages} />
{token && <Input message={message} setMessage={setMessage} sendMessage={sendMessage} />}
</div>
</div>
);
};
export default withRouter(Chat);
I can give u the advice to make a chat app with socket.io, not setInterval.
https://socket.io/get-started/chat

Why am I unable to set an array in useEffect?

I am learning React and trying to write an asynchronous hook. Using setResult inside of useEffect doesn't seem to work. When I tried to render the result, there was nothing, so I added some console logging to see what is going on. The setter function in the useState hook doesn't seem to be doing anything. I've been following this video for some guidance, and my code does not differ too much.
I have the following component:
import React, { useState, useEffect } from 'react'
const Search = () => {
const [search, setSearch] = useState('')
const [query, setQuery] = useState('')
const [result, setResult] = useState([])
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
`https://api.spotify.com/v1/search?q=${encodeURIComponent(
query
)}&type=track`,
{
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Bearer ' + auth.access_token
}
}
)
const json = await response.json()
console.log({ json })
console.log(
json.tracks.items.map(item => {
return item.id
})
)
setResult(
json.tracks.items.map(item => {
return item.id
})
)
console.log({result})
} catch (error) {
console.log(error)
}
}
if (query !== '') {
fetchData()
}
}, [query])
return (
<div>
<input
value={search}
placeholder='Search...'
onChange={event => setSearch(event.target.value)}
onKeyPress={event => {
if (event.key === 'Enter') {
setQuery(search)
}
}}
></input>
<br />
{result.map(item => (
<h3 key={item}></h3>
))}
</div>
)
}
export default Search
From console.log ({ json }), I see the response from the server looks OK.
console.log(
json.tracks.items.map(item => {
return item.id
})
)
The above console output looks OK as well.
setResult(
json.tracks.items.map(item => {
return item.id
})
)
console.log({result})
Why is result empty?
EDIT: Thanks Patrick and Talgat. I understand now. So, when I console.log outside of useEffect, I could see result is set correctly. I then realized I was missing a reference to {item} in my render:
{result.map(item => (
<h3 key={item}>{item}</h3>
))}
Now I see the IDs rendered on the page. Thanks for your help.
setTimeout(()=>{
console.log(result);
},0);
Please try this instead using console.log(result).
Setting state is not updated as soon as you insert value via setter( on your side setResult ).
So to do it, delay is needed.

react-dom.production.min.js:171 RangeError: Maximum call stack size exceeded

I've searched through the entire web to resolve that recursive issue but could not find anything...
In the majority of the case, that issue occurs when calling setState() in didUpdate() function but it's not my case.
Initially I was using react v15, my app was working well on both
environments, development and production.
Lately I decided to upgrade to react v16, and also upgrade to node 8.12.0.
At this point my app was working perfectly fine when NODE_ENV=development, but when I deployed on production(NODE_ENV=production), I started to get that strange issue, thrown, in the console of my chrome browser
I've found out that if I downgrade react to version 15, my app started to work again on production.
During my investigation I've tested that, dispatching too many actions triggers that "Maximum call stack size exceeded" issue.
Why would my code works fine when NODE_DEVELOPMENT and crash when NODE_ENV- production ?
Could anyone give me some help?
Please find my code: ContainerPage.js
class ContainerPage extends Component {
constructor(props) {
super(props);
const { locale } = props;
moment.locale(locale);
}
componentDidMount() {
const {
getGeometry,
getCountries,
getNodes,
geometry = [],
location: { query = {} } = {},
} = this.props;
const { INIT_CORPORATION_TREE } = actions;
if (geometry.length === 0) {
getCountries();
getGeometry();
}
getNodes(
{
action: INIT_CORPORATION_TREE,
corporationIdQuery: !isEmpty(query) ? query.corporationId : null,
});
}
render() {
const { content,
children,
header,
commonProfileSetup,
selectedCorporation = {},
riskConfig: { riskConfigInitialized = false } = {},
} = this.props;
if (!isEmpty(selectedCorporation.id) && riskConfigInitialized) {
return (
<Col md={12}>
{content || children}
</Col>
);
}
return <div>test</div>;
}
}
export default withRouter(ContainerPage);
Routes.js
export default (
<Route component={App} onChange={scrollToTop}>
<Route path="/nui">
<Redirect
from={config.nui.baseUrl}
to={config.nui.baseUrl + '/search'}
/>
<Route
key="locate"
path="locate"
component={NuiPageContainer}
>
<Route getResponseHeaders={() => ({ 'X-Frame-Options': 'DENY' })}
component={UserIsAuthorized(ContainerPage)}>
<Route
path="search"
getComponents={(loc, cb) => {
require.ensure([], require => {
cb(null, {
header: () => <Header titleId="title._dynamic.search" />,
//commonProfileSetup: () => <CommonProfileSetup pageReducer="search" treeReducer="corporationTree" showTree />,
//content: ({}) => <SearchPage pageReducer={PAGE_REDUCER.SEARCH} />,
});
});
}}
/>
</Route>
</Route>
</Route>
</Route>
Dispatch actions
export const getCountries = withJWT(
createFetchAction(
GET_COUNTRIES,
`${RM_BASE_URI}/country/countries`,
getOptions
)
);
// Getting geometry from local file
export const getGeometry = () => {
return {
type: GET_GEOMETRY,
payload: GEOMETRY,
};
};
export function getNodes(param = { }, actions) {
return (dispatch, getState, parentState) => {
const { selectedNode } = getState()[parentState];
const {
JWT: { token },
routing: { locationBeforeTransitions: { pathname = '' } = {} } = {},
userPrefs: { loginName }
} = getState();
const {
FETCHING_NODES,
RESET_TREE,
HIGHLIGHT_SELECTED_CORPORATION,
SET_CORPORATION_PROGRAMMATICALLY,
SET_CORPORATION_FROM_DB,
INIT_CORPORATION_TREE,
} = actions;
let url;
url = `${config.nui.riskMessaging.uri}/corporation/default?concurLoginId=${loginName}`;
if (!isEmpty(param.corporationIdQuery)) {
url = `${url}&selectedCorporation=${param.corporationIdQuery}`;
if (pathname.includes('details')) {
url = `${url}&subLevelsOnly=true`;
}
}
const { action = '' } = param;
const onCompleteAction = action;
const loadingAction = FETCHING_NODES;
return fetch(url, {
method: 'GET',
mode: 'cors',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
'Accept': 'application/json',
'Access-Control-Allow-Origin': '*'
}
})
.then(response => {
if (response.status === 200) {
return response.json();
}
})
.then(result => {
const { corporationTree: { corporations } } = getState();
dispatch(createAction('INIT_CORPORATION_TREE')(result));
})
.catch(function (err) {
console.log("Oops... " + err);
});
};

Resources