React real-time chart with react.context and socket.io - reactjs

I'm trying to build a website for displaying real-time charts in typescript with react for my learning. But I can not get values from a server properly before displaying the chart.
What I want to do is ...
Communication protocol is websocket using socket.io.
Using socket.io and storing data are inside React.Context(useSocket.tsx) so as to access the data from any react components easily.
Displaying the data is in Home.tsx.
The socket events are initial_data and new_data.
The initial_data event is received at the time the accessing the website at first.
The new_data event is received at regularly.
The time getting both events above, update values inside Home.tsx automatically.
I researched some articles on the web, for example, explaining a way that using socket.io inside a useEffect() function that returning socket.disconnect().
So, the code I built is below.
useSocket.tsx
import {useContext, createContext, useState, useEffect} from "react";
import {io, Socket} from "socket.io-client";
import {chartDataType} from "../types/chartDataType";
type Context = {
chartData: Array<chartDataType>;
}
const SocketContext = createContext<Context>({
chartData: [],
});
const SocketsProvider = (props: any) => {
const [chartData, setChartData] = useState();
useEffect( () => {
const socket: Socket = io("http://***.***.***.***");
socket.on('initial_data', (data) => {
console.log(data);
setChartData(data);
});
socket.on('new_data', (data) => {
console.log(data);
});
return () => { socket.disconnect() };
},[]);
return (
<SocketContext.Provider value={{chartData}} {...props} />
);
}
const useSocket = () => useContext(SocketContext);
export { SocketsProvider, useSocket };
Home.tsx
import {memo, VFC} from "react";
import { useSocket } from "../../context/useSocket";
import {Heading} from "#chakra-ui/react";
export const Home: VFC = memo(() => {
const { chartData } = useSocket();
return (
<>
<Heading as="h1">{`${chartData}`}</Heading>
</>
)
})
The above code caused an error Uncaught TypeError: Cannot read properties of undefined (reading '0') occurred in the browser console. But when the comment out the <Heading>...</Heading> line in Home.tsx, the console.log in useSocket.tsx can display the value from the server in the browser console.
I can not come up with the idea for the correct implementation. Is the definition of the type of the chartData wrong? or other reasons? The definition of the chartDataType has nothing wrong.
What is the way for the correct implementation?

What's happening is you are trying to render an empty array, the data hasn't loaded yet.
You need to check if charData exists, or if it's undefined first.
Like this:
return (
{CharData ? <Heading /> ... : null }
)

Related

Why is the `question` property in the API not reachable and why is questionBank not rendering?

Just to let you know, I don't yet know how to use class based components, setState, etc. I also don't know how to use other things in async js like axios or whatever else yet. This is what I can do below. Very basic.
This is App.js:
import Questions from './components/Questions.js'
import './index.css'
import {React, useState, useEffect} from 'react'
function App() {
const [questions, setQuestions] = useState([])
useEffect(() => {
async function getQuestions(){
const response = await fetch("https://opentdb.com/api.php?amount=5")
const data = await response.json()
setQuestions(() => data.results)
}
getQuestions()
}, [])
const questionBank = questions.map(singleQuestion => {
<Questions
question={singleQuestion.question}
/>
})
console.log(questions[0].question)
return (
<div>
{questionBank}
</div>
);
}
export default App;
For some reason, console.log(questions[0].question) when typed in to the editor and saved for the first time, it shows a question from the api. But after refreshing the page it doesn't show a question but it says: App.js:44 Uncaught TypeError: Cannot read properties of undefined (reading 'question') But when I just do this: console.log(questions[0]), it shows the first object of the array from the API no problem. I'm confused.
Also, questionBank doesn't render at all for some reason.
This is Questions.js:
import React from 'react'
export default function Questions(props){
return(
<div>
<p>{props.question}</p>
<br />
</div>
)
}
This is index.js:
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(
<App />
)
There are few things to improve in your code (such as maybe make a useCallback out of function getQuestions in the useEffect), but the biggest issue is that you are not properly returning JSX from the map method.
Your code:
const questionBank = questions.map(singleQuestion => {
<Questions
question={singleQuestion.question}
/>
})
notice the curly braces { & }. The proper code:
const questionBank = questions.map(singleQuestion => (
<Questions
question={singleQuestion.question}
/>
))
After this change, your code should work properly.
Also, your console.log will cause errors, because you are console.logging before even fetching theses questions, so obviously questions[0] is undefined.
More improvements to the code:
export default function App() {
const [questions, setQuestions] = useState([]);
const getQuestions = useCallback(async () => {
const response = await fetch("https://opentdb.com/api.php?amount=5");
const data = await response.json();
setQuestions(data.results);
}, []);
useEffect(() => {
getQuestions();
}, [getQuestions]);
const questionBank = questions.map((singleQuestion) => (
<Questions question={singleQuestion.question} />
));
return <div>{questionBank}</div>;
}

React Native Unable to fetch multiple documents data from Firebase firestore

App.js File this is App.js file, Please check the below code and correct me I don't have any idea how to solve this issue. Thank you in advance.
.doc("2E3kDAa0QzqoQuoBK1T9", "vROxKUSgcNWwlt7cDEKV") on this line I get the issue.
import React, { useState, useEffect } from "react";
import { View, Text } from 'react-native';
import firestore from '#react-native-firebase/firestore';
function App() {
const [myData, setMyData] = useState(null);
useEffect(() => {
getDatabase();
}, []);
const getDatabase = async () => {
try {
const data = await firestore()
.collection('myCollection')
.doc("2E3kDAa0QzqoQuoBK1T9", "vROxKUSgcNWwlt7cDEKV")
.get();
setMyData(data._data)
} catch (err) {
console.log(err);
}
};
return (
<View>
<Text>First:-{myData ? myData.name : 'Loading...'}</Text>
<Text>Second:-{myData ? myData.secondData : 'Loading...'}</Text>
</View>
);
}
export default App;
This doesn't work:
.doc("2E3kDAa0QzqoQuoBK1T9", "vROxKUSgcNWwlt7cDEKV")
If you check the API docs for CollectionReference.doc it expects:
doc(documentPath?: undefined | string): DocumentReference<>;
So you can pass in a single document ID or path, not multiple.
If you want to return multiple documents by their ID, you can use th in query operator on the FieldPath.documentId() to do so for up to 10 values. If you have more values, you'll need to retrieve them in batches of up to 10 and merge them in your application code.

React Component Re-execute incorrectly

In my simple react app there two component: App and Comments.
App.js =>
import Comments from "./comments/Comments";
const App = () => {
return (
<div>
<h1>App.js</h1>
<Comments currentUserId="1" />
</div>
);
};
export default App;
Comments.js =>
import { useState, useEffect } from "react";
import { getComments as getCommentsApi } from "../api";
const Comments = ({ currentUserId }) => {
const [comments, setComments] = useState([]);
console.log("Comments", comments);
useEffect(() => {
getCommentsApi()
.then(data => {
setComments(data);
})
}, []);
return (
<div>
<h1>Comments.js</h1>
</div>
);
};
export default Comments;
If you take a look at my Comments Component, there im logging the Comments in the function body and it should be execute two time, first for the initial load and the second time due to the useEffect and changing the state.
React Components Should Re-execute when props or state changed
But 4 logs appear in my console. Why?
I think you are using strict Mode, that's why you are getting the unexpected result. Remove the Strict Mode component from the index.js file and the code will work as expected

When testing, code that causes React state updates should be wrapped into act(...) - with simple react-native nested screen/components with jest axios

I am new to unit testing/jest, but I know some about react native.
I want to write a test for my HomeScreen, which contains a component that makes a simple request. The code runs without any issue but fails when I run it with Jest.
HomeScreen.js
import { View } from 'react-native'
import APIExample from '#components/Examples/APIExample'
const HomeScreen = () => {
return (<View> <APIExample /> </View>)
}
export default HomeScreen
HomeScreen.test.js
import { render } from '#testing-library/react-native'
import HomeScreen from '#screens/HomeScreen'
it('should run', async () => {
const { getByText } = await render(<HomeScreen />)
})
APIExample.js
import { useState, useEffect } from 'react'
import { Text, View } from 'react-native'
import API from '../../API'
const APIExample = () => {
const [apiResponse, setApiResponse] = useState(null)
const Submit = async () => {
const response = await API.Test()
setApiResponse(response)
}
useEffect(() => {
Submit()
}, [])
return (
<View>
<Text>
{JSON.stringify(apiResponse)}
</Text>
</View>
)
}
export default APIExample
I try to figure out why does it keep saying that I should wrap it in act and what exactly do I need to wrap?
I already tried to wrap the render whole line but had no success.
The API.Test is a simple axios.get
The error I've kept getting is:
Warning: An update to APIExample inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
It happened to me with fireEvent a couple of days ago. Try this:
await waitFor(()=> render(<HomeScreen />))
The reason you're facing the issue is because of state change that's happening. While on first render the apiResponse data is set as null. And later with the api response the apiResponse has a value so there is re-render that has occured, so jest complains about it.
To resolve you could use await waitFor(() => expect(api).toHaveBeenCalledTimes(1)). This will wait for a specific period of time.
Suggestion: Mock your api's in tests, instead of hitting it directly.

React Hooks - function inside function component passed as prop can't access state

I have read about React hooks and wanted to try it on this new website I have been developing. I am trying to pass an async function as a prop. Part of the function is to set my "cities" state and console log just to see if the API works. I have been receiving "setCities is not a function" error or, cities returns undefined if I just console log it directly. I think the function can't access the {cities, setCities} state. I have tried it in class component and it works. Has anyone encountered similar situations? Below is the code sample:
import React, { useState } from "react";
import axios from "axios";
import Menu from "./Menu";
function App() {
const { cities, setCities } = useState([]);
const searchSubmit = async term => {
try {
const res = await axios.get("http://localhost:8080/general/search", {
params: { city: term }
});
setCities(res.data);
console.log(cities);
} catch (err) {
console.log(err.message);
}
};
return (
<div className="ui container">
<Menu handleSearchSubmit={searchSubmit} />
</div>
);
}
useStates return an array, not an object...
So you can change
const { cities, setCities } = useState([]);
to
const [ cities, setCities ] = useState([]);
And it should work.

Resources