React cache google places api search results - reactjs

So i have the following functional component that works fine. What i want to do is cache the results so i am not hitting the api over and over again for same search term.
import React, { useState, useEffect, useRef } from "react";
import currentSession from "../currentSession";
let autoComplete;
const loadScript = (url, callback) => {
let script = document.createElement("script");
script.type = "text/javascript";
if (script.readyState) {
script.onreadystatechange = function () {
if (script.readyState === "loaded" || script.readyState === "complete") {
script.onreadystatechange = null;
callback();
}
};
} else {
script.onload = () => callback();
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
};
function handleScriptLoad(updateQuery, autoCompleteRef) {
autoComplete = new window.google.maps.places.Autocomplete(
autoCompleteRef.current,
{ types: ["(cities)"] }
);
autoComplete.setFields(["address_components", "formatted_address"]);
autoComplete.addListener("place_changed", () =>
handlePlaceSelect(updateQuery)
);
}
async function handlePlaceSelect(updateQuery) {
const addressObject = autoComplete.getPlace();
const query = addressObject.formatted_address;
updateQuery(query);
}
function SearchLocationInput(props) {
const [query, setQuery] = useState("");
const autoCompleteRef = useRef(null);
useEffect(() => {
loadScript(
"https://maps.googleapis.com/maps/api/js?key=" +
currentSession.getGoogleApiKey() +
"&libraries=places",
() => handleScriptLoad(setQuery, autoCompleteRef)
);
}, []);
return (
<div className="search-location-input">
<input
ref={autoCompleteRef}
onChange={(event) => {
props.onCityChange(event.target.value);
setQuery(event.target.value);
}}
placeholder="Enter a City"
value={query}
/>
</div>
);
}
export default SearchLocationInput;
I am assuming the request is occurring in the loaded script so i am not sure how i can accomplish this?

Related

ontrack is not firing in webrtc

I am making a video calling application using react , webrtc and firebase. I am able to connect both local and remote connection but i am not receiving the remote mediastream. The ontrack function is not firing and i don't know why.
import logo from './logo.svg';
import { useEffect, useRef } from 'react';
import './App.css';
import { useState } from 'react';
import { initializeApp } from 'firebase/app'
import { addDoc, collection, doc, getDoc, getFirestore, onSnapshot, setDoc, updateDoc } from 'firebase/firestore'
function App() {
const myVideoRef = useRef(null)
const yourVideoRef = useRef(null)
const [cameras, setcameras] = useState()
const [myCallId, setmyCallId] = useState()
const [callId, setcallId] = useState()
const [roomId ,setroomId] = useState()
const servers = {
iceServers: [
{
urls: ['stun:stun1.l.google.com:19302', 'stun:stun2.l.google.com:19302'],
},
],
iceCandidatePoolSize: 10,
}
const pc = new RTCPeerConnection(servers)
const firebaseConfig = {
// my config
}
const app = initializeApp(firebaseConfig)
const db = getFirestore(app)
async function getConnectedDevices(type){
const devices = await navigator.mediaDevices.enumerateDevices()
return devices.filter(device => device.kind === type)
}
async function openCamera(cameraId){
const constraints = {
'audio': {'echoCancellation': true},
'video': {
'deviceId': cameraId,
}
}
navigator.mediaDevices.getUserMedia(constraints)
.then((stream) => {
stream.getTracks().forEach((track) => {
pc.addTrack(track,stream)
})
myVideoRef.current.srcObject = stream
})
// this part is not firing.
let remoteStream = new MediaStream()
pc.ontrack = (event) => {
event.streams[0].getTracks().forEach((track) => {
remoteStream.addTrack(track)
})
}
yourVideoRef.current.srcObject = remoteStream
}
async function call(){
const callDoc = doc(collection(db,"calls"))
const offerCandidates = collection(callDoc,"offerCandidates")
const answerCandidates = collection(callDoc,"answerCandidates")
setmyCallId(callDoc.id)
pc.onicecandidate = (event) => {
console.log(event.candidate)
event.candidate && addDoc(offerCandidates,event.candidate.toJSON())
}
const offerDescription = await pc.createOffer()
await pc.setLocalDescription(offerDescription)
const offer = {
sdp: offerDescription.sdp,
type: offerDescription.type
}
await setDoc(callDoc, { offer } )
onSnapshot(callDoc, snapshot => {
const data = snapshot.data()
if(!pc.currentRemoteDescription && data?.answer){
const answerDescription = new RTCSessionDescription(data.answer)
pc.setRemoteDescription(answerDescription)
}
})
onSnapshot(answerCandidates, snapshot => {
snapshot.docChanges().forEach((change) => {
if(change.type === 'added'){
console.log(change.doc.data())
const candidate = new RTCIceCandidate(change.doc.data())
pc.addIceCandidate(candidate)
console.log(yourVideoRef.current.srcObject)
console.log(myVideoRef.current.srcObject)
}
})
})
}
async function answer(){
const callDoc = doc(collection(db,"calls"),callId)
const offerCandidates = collection(callDoc,"offerCandidates")
const answerCandidates = collection(callDoc,"answerCandidates")
pc.onicecandidate = (event) => {
event.candidate && addDoc(answerCandidates,event.candidate.toJSON())
}
const callData = (await getDoc(callDoc)).data()
console.log(callData)
const offerDescription = callData.offer
await pc.setRemoteDescription(new RTCSessionDescription(offerDescription))
const answerDescription = await pc.createAnswer()
await pc.setLocalDescription(answerDescription)
const answer = {
type: answerDescription.type,
sdp: answerDescription.sdp,
}
await updateDoc(callDoc, {answer})
onSnapshot(offerCandidates, snapshot => {
snapshot.docChanges().forEach((change) => {
console.log(change)
if(change.type === 'added'){
let data = change.doc.data()
pc.addIceCandidate(new RTCIceCandidate(data))
}
})
})
}
useEffect(() => {
async function fetch(){
const cameras = await getConnectedDevices('videoinput')
setcameras(cameras)
}
fetch()
},[])
return (
<div className="App">
<div className='flex flex-row'>
{cameras && cameras.map( camera => <h2 className='m-3' key={camera.deviceId} onClick={() => openCamera(camera.deviceId)} >{camera.label}</h2>)}
</div>
<div className='flex flex-row'>
<video className='w-1/2' ref={myVideoRef} autoPlay />
<video className='w-1/2' ref={yourVideoRef} autoPlay />
</div>
<button onClick={call} className='bg-gray-200'>Call</button>
<div className='flex flex-row'>
<input onChange={(e) => setcallId(e.target.value)} className='border-[4px] border-black' />
<button onClick={answer} className='bg-gray-200'>Answer</button>
</div>
<h2>{myCallId}</h2>
</div>
);
}
export default App;
The ontrack function is not firing. I have put the function in useEffect , on top outside all the functions, before setremotedescription but it is still not firing.
let remoteStream = new MediaStream()
pc.ontrack = (event) => {
event.streams[0].getTracks().forEach((track) => {
remoteStream.addTrack(track)
})
}
yourVideoRef.current.srcObject = remoteStream

Custom hook in React JS : arguments not updating after rerender

I have a date range filter in which when submitted, it will update new values of dateStart and dateEnd using setDateStart and setDateEnd then it will pass down the new values to my useCollection custom hook. Why is it that when it rerenders, the useCollection custom hook arguments doesn't get the updated values?
*the custom useCollection hook is used for data Fetching on Firebase
*the dateStart and dateEnd is used for filtering the data displayed on the screen
useCollection custom hook:
import { useEffect, useState, useRef } from "react";
import { projectFirestore } from "../firebase/config";
export const useCollection = (
collection,
_query,
_query2,
_query3,
_orderBy
) => {
const [documents, setDocuments] = useState(null);
const [error, setError] = useState(null);
// if we don't use a ref --> infinite loop in useEffect
// _query is an array and is "different" on every function call
const query = useRef(_query).current;
const query2 = useRef(_query2).current;
const query3 = useRef(_query3).current;
const orderBy = useRef(_orderBy).current;
console.log("from query: " + query);
console.log("from query2: " + query2);
console.log("from query2: " + query3);
useEffect(() => {
let ref = projectFirestore.collection(collection);
if (query) {
ref = ref.where(...query);
}
if (query2) {
ref = ref.where(...query2);
}
if (query3) {
ref = ref.where(...query3);
}
if (orderBy) {
ref = ref.orderBy(...orderBy);
}
const unsubscribe = ref.onSnapshot(
(snapshot) => {
let results = [];
snapshot.docs.forEach((doc) => {
results.push({ ...doc.data(), id: doc.id });
});
// update state
setDocuments(results);
setError(null);
},
(error) => {
console.log(error);
setError("could not fetch the data");
}
);
// unsubscribe on unmount
return () => unsubscribe();
}, [collection, query, query2, query3, orderBy]);
return { documents, error };
};
TimeCard.js
import React, { useState, useEffect } from "react";
import moment from "moment";
import { useAuthContext } from "../../hooks/useAuthContext";
import { useCollection } from "../../hooks/useCollection";
import Table from "../../components/Table";
import DateRange from "../../components/DateRange";
import ErrorMessage from "../../components/ErrorMessage";
const TimeCard = () => {
const { uid } = useAuthContext().user;
let m1 = moment(new Date());
let m2 = moment();
m1.startOf("month").startOf("day");
m2.endOf("day");
const [dateStart, setDateStart] = useState(m1.toDate());
const [dateEnd, setDateEnd] = useState(m2.toDate());
console.log("Moment1 from State: ", dateStart);
console.log("Moment2: from State", dateEnd);
const [time1, setTime1] = useState("");
const [time2, setTime2] = useState("");
const [error, setError] = useState("");
console.log("RENDER");
const { documents } = useCollection(
"timeinout",
["uid", "==", uid],
["createdAt", ">=", dateStart],
["createdAt", "<=", dateEnd],
["createdAt", "asc"]
);
const handleSubmit = (e) => {
e.preventDefault();
var d1 = new Date(time1);
var d2 = new Date(time2);
if (!time1 || !time2) {
setError(
"Either ONE or BOTH of the date inputs below are empty, please select a date!"
);
return null;
}
if (d1.getTime() > d2.getTime()) {
setError(
"Invalid Date: Date 2 (To:) Input is greater than Date 1 (From:) Input"
);
return null;
}
const s1 = moment(time1).startOf("day").toDate();
const s2 = moment(time2).endOf("day").toDate();
setDateStart(s1);
setDateEnd(s2);
};
useEffect(() => {
const time = setTimeout(() => {
setError("");
}, [5000]);
return () => {
clearTimeout(time);
};
}, [error]);
return (
<>
<div className="flex flex-col">
{error && <ErrorMessage msg={error} />}
<DateRange
time1={time1}
time2={time2}
setTime1={setTime1}
setTime2={setTime2}
handleSubmit={handleSubmit}
/>
</div>
{documents && <Table dataFromDatabase={documents} />}
</>
);
};
export default TimeCard;
Implemented it without using a custom hook instead, basically all the codes from the useCollection custom hook were transferred to TimeCard.js and created a normal useEffect hook that process all the changes in the filter for the dates.
See code below:
import React, { useState, useEffect } from "react";
import { projectFirestore } from "../../firebase/config";
import moment from "moment";
import { useAuthContext } from "../../hooks/useAuthContext";
import Table from "../../components/Table";
import DateRange from "../../components/DateRange";
import ErrorMessage from "../../components/ErrorMessage";
const TimeCard = () => {
const { uid } = useAuthContext().user;
const [error, setError] = useState("");
const [documents, setDocuments] = useState(null);
const startOfMonth = moment().startOf("month").format("YYYY-MM-DD");
const endOfMonth = moment().endOf("month").format("YYYY-MM-DD");
console.log("startOfMonth: ", startOfMonth);
console.log("endOfMonth: ", endOfMonth);
const [time1, setTime1] = useState(startOfMonth);
const [time2, setTime2] = useState(endOfMonth);
const [query] = useState(["uid", "==", uid]);
const [query2, setQuery2] = useState([
"createdAt",
">=",
moment(time1).startOf("day").toDate(),
]);
const [query3, setQuery3] = useState([
"createdAt",
"<=",
moment(time2).endOf("day").toDate(),
]);
const [orderBy] = useState(["createdAt", "asc"]);
const handleSubmit = (e) => {
e.preventDefault();
var d1 = new Date(time1);
var d2 = new Date(time2);
if (!time1 || !time2) {
setError(
"Either ONE or BOTH of the date inputs below are empty, please select a date!"
);
return null;
}
// Loseless code below because of > comparison operator
if (d1.getTime() > d2.getTime()) {
setError(
"Invalid Date: Date 2 (To:) Input is greater than Date 1 (From:) Input"
);
return null;
}
setQuery2(["createdAt", ">=", moment(time1).startOf("day").toDate()]);
setQuery3(["createdAt", "<=", moment(time2).endOf("day").toDate()]);
};
useEffect(() => {
const time = setTimeout(() => {
setError("");
}, [5000]);
return () => {
clearTimeout(time);
};
}, [error]);
useEffect(() => {
let ref = projectFirestore.collection("timeinout");
if (query) {
ref = ref.where(...query);
}
if (query2) {
ref = ref.where(...query2);
}
if (query3) {
ref = ref.where(...query3);
}
if (orderBy) {
ref = ref.orderBy(...orderBy);
}
const unsubscribe = ref.onSnapshot(
(snapshot) => {
let results = [];
snapshot.docs.forEach((doc) => {
results.push({ ...doc.data(), id: doc.id });
});
// update state
setDocuments(results);
setError(null);
},
(error) => {
console.log(error);
setError("could not fetch the data");
}
);
// unsubscribe on unmount
return () => unsubscribe();
}, [query, query2, query3, orderBy]);
console.log("Updated documents OUTSIDE: ", documents);
return (
<>
<div className="flex flex-col">
{error && <ErrorMessage msg={error} />}
<DateRange
time1={time1}
time2={time2}
setTime1={setTime1}
setTime2={setTime2}
handleSubmit={handleSubmit}
/>
</div>
{documents && <Table documents={documents} />}
</>
);
};
export default TimeCard;

Trying to implement debounce in React Project

I'm trying to implement debounce in a small/test React application.
It's just an application that fetch data from an API and it has a text field for an auto complete.
import React, { useEffect, useState, useMemo } from 'react';
import axios from 'axios';
const API = 'https://jsonplaceholder.typicode.com/posts';
const AutoComplete2 = () => {
const [ text, setText ] = useState("")
const [ posts, setPosts ] = useState([])
useEffect(() => {
async function fetchData() {
const data = await axios.get(API);
if(parseInt(data.status) !== 200) return;
setPosts(data.data)
}
fetchData();
}, [])
const handleTextChange = (event) => setText(event.target.value);
const handleSelectOption = (str) => setText(str);
const showOptions = useMemo(() => {
if(text === '') return;
const showPosts = [...posts].filter((ele) => ele.title.toLowerCase().includes(text.toLowerCase()));
if(showPosts.length === 1) {
setText(showPosts[0].title);
} else {
return (
<div>
{showPosts.map((obj, index) => {
return (
<div key={index} >
<span onClick={() => handleSelectOption(obj.title)} style={{cursor: 'pointer'}}>
{obj.title}
</span>
</div>
)
})}
</div>
)
}
}, [text, posts])
// addding debounce
const debounce = (fn, delay) => {
let timer;
return function() {
let context = this;
let args = arguments;
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(context, args)
}, delay);
}
}
const newHandleTextChange = ((val) => debounce(handleTextChange(val), 5000));
return (
<div>
<input type="text" value={text} onChange={newHandleTextChange} />
{showOptions}
</div>
)
}
export default AutoComplete2;
The application works, but not the debounce. I add a 5 seconds wait to clearly see if it is working, but every time I change the input text, it calls the function without the delay. Does anyone know why it is happening?
Thanks
A more idiomatic approach to debouncing in React is to use a useEffect hook and store the debounced text as a different stateful variable. You can then run your filter on whatever that variable is.
import React, { useEffect, useState, useMemo } from "react";
import axios from "axios";
const API = "https://jsonplaceholder.typicode.com/posts";
const AutoComplete2 = () => {
const [text, setText] = useState("");
const [debouncedText, setDebouncedText] = useState("");
const [posts, setPosts] = useState([]);
useEffect(() => {
async function fetchData() {
const data = await axios.get(API);
if (parseInt(data.status) !== 200) return;
setPosts(data.data);
}
fetchData();
}, []);
// This will do the debouncing
// "text" will always be current
// "debouncedText" will be debounced
useEffect(() => {
const timeout = setTimeout(() => {
setDebouncedText(text);
}, 5000);
// Cleanup function clears timeout
return () => {
clearTimeout(timeout);
};
}, [text]);
const handleTextChange = (event) => setText(event.target.value);
const handleSelectOption = (str) => setText(str);
const showOptions = useMemo(() => {
if (debouncedText === "") return;
const showPosts = [...posts].filter((ele) =>
ele.title.toLowerCase().includes(debouncedText.toLowerCase())
);
if (showPosts.length === 1) {
setText(showPosts[0].title);
} else {
return (
<div>
{showPosts.map((obj, index) => {
return (
<div key={index}>
<span
onClick={() => handleSelectOption(obj.title)}
style={{ cursor: "pointer" }}
>
{obj.title}
</span>
</div>
);
})}
</div>
);
}
}, [debouncedText, posts]);
return (
<div>
<input type="text" value={text} onChange={handleTextChange} />
{showOptions}
</div>
);
};
export default AutoComplete2;
import { useEffect, useState, useRef } from "react";
import axios from "axios";
import { backend_base_url } from "../constants/external_api";
export default function DebounceControlledInput() {
const [search_category_text, setSearchCategoryText] = useState("");
let debounceSearch = useRef();
useEffect(() => {
const debounce = function (fn, interval) {
let timer;
return function (search_key) {
clearTimeout(timer);
timer = setTimeout(() => {
fn(search_key);
}, interval);
};
};
const getCategories = function (search_key) {
axios
.get(`${backend_base_url}categories/${search_key}`)
.then((response) => {
console.log("API Success");
})
.catch((error) => {});
};
debounceSearch.current = debounce(getCategories, 300);
//use for initial load
//debounceSearch.current('');
}, []);
const searchCategory = (search_key) => {
debounceSearch.current(search_key);
};
return (
<form
className="form-inline col-4"
onSubmit={(e) => {
e.preventDefault();
}}
autoComplete="off"
>
<input
type="text"
placeholder=""
id="search"
value={search_category_text}
onChange={(e) => {
searchCategory(e.target.value);
setSearchCategoryText(e.target.value);
e.preventDefault();
}}
/>
</form>
);
}

How can I upload a image file using and react Socket-IO?

i want to make a chat app with react and socket.io
i downloaded code from https://github.com/adrianhajdin/project_chat_application.
I want to add upload file function using socketio_file_upload
but when i upload the img. there is nothing happen..
this is my code
index.js (server)
const express = require('express');
const socketio = require('socket.io');
const http = require('http');
const siofu = require("socketio-file-upload");
const { addUser, removeUser, getUser, getUsersInRoom } = require('./users');
const PORT = process.env.PORT || 5000
const router = require('./router')
const app = express()
console.log(app,'------------------app----------')
const server = http.createServer(app)
const io = socketio(server)
io.on('connection', (socket)=>{
const uploader = new siofu();
uploader.dir = "/uploads";
uploader.listen(socket);
uploader.on("saved", function(event){
});
uploader.on("error", function(event){
});
socket.on('join', ({name, room}, callback)=>{
const { user, error } = addUser({ id:socket.id, name, room })
if(error) {
return callback(error)
}
//admin system정보 여기서
socket.emit('message', { user:'admin', text:`${user.name}, welcome to the room ${user.room}` })
//braodcast는 room에 있는 모든 사람에게 msg를 보낸다.
socket.broadcast.to(user.room).emit('message', { user: 'admin', text: `${user.name} has joined!` });
socket.join(user.room)
io.to(user.room).emit('roomData', {room: user.room, users: getUsersInRoom(user.room)})
callback()
})
socket.on('sendMessage', (message, callback) => {
const user = getUser(socket.id);
io.to(user.room).emit('message', { user: user.name, text: message });
io.to(user.room).emit('roomData', { room: user.room, users: getUsersInRoom(user.room)});
callback();
});
socket.on('disconnect', () => {
const user = removeUser(socket.id)
io.to(user.room).emit('message', { user:'admin', text: `${user.name} has left`})
})
})
app.use(siofu.router)
server.listen(PORT, ()=> console.log('---SERVER START---'))
Chat.jsx
import React, { useState, useEffect, useRef } from 'react';
import queryString from 'query-string'
import io from 'socket.io-client'
import './chat.css'
import InfoBar from '../InfoBar/InfoBar'
import Messages from '../Messages/Messages'
import Input from '../Input/Input'
let socket ='default'
const Chat = ({location}) => {
const [name, setName] = useState('');
const [room, setRoom] = useState('');
const [message, setMessage] = useState('')
const [messages, setMessages] = useState([])
const END_POINT = 'localhost:5000'
//componentdidmount와 같다
useEffect(()=>{
const {name, room} = queryString.parse(location.search)
socket = io(END_POINT)
setName(name)
setRoom(room)
//server에 join을 통해서 데이터를 보냄
socket.emit('join', {name, room}, (res)=>{
})
//useEffect()애서 return 은 unmount와 같다. 즉 user leave일 때 사용!
return () =>{
socket.emit('disconnect')
socket.off()
}
},[END_POINT, location.search])
useEffect(() => {
socket.on('message', (message) =>{
setMessages([...messages, message])
})
// uploader.listenOnInput(document.getElementById("siofu_input"));
}, [messages])
const sendMessage = (event) => {
event.preventDefault();
if(message) {
socket.emit('sendMessage', message, () => setMessage(''));
}
}
return (
<div className="outerContainer">
<div className="container">
<InfoBar room={room} />
<Messages messages={messages} name={name} />
<Input socket={socket} message={message} setMessage={setMessage} sendMessage={sendMessage} />
</div>
</div>
);
};
export default Chat;
Input.jsx
import React, { Component } from 'react';
import SocketIOFileUpload from '../../../../server/node_modules/socketio-file-upload'
import './input.css';
class Input extends Component {
uploadFile = (e) => {
const {socket} = this.props
console.log("uploading");
// const files = e.target.files;
const siofu = new SocketIOFileUpload(socket);
siofu.listenOnInput(document.getElementById("siofu_input"));
};
render() {
const { setMessage, sendMessage, message, socket } = this.props
return (
<form className="form">
<input
className="input"
type="text"
placeholder="Type a message..."
value={message}
onChange={({ target: { value } }) => setMessage(value)}
onKeyPress={event => event.key === 'Enter' ? sendMessage(event) : null}
/>
<input type="file" id="siofu_input" onChange={this.uploadFile.bind(this)}/>
<button className="sendButton" onClick={e => sendMessage(e)}>Send</button>
</form>
);
}
}
export default Input;
users.js
const users = [];
const addUser = ({ id, name, room }) => {
name = name.trim().toLowerCase();
room = room.trim().toLowerCase();
const existingUser = users.find((user) => user.room === room && user.name === name);
if(!name || !room) return { error: 'Username and room are required.' };
if(existingUser) return { error: 'Username is taken.' };
const user = { id, name, room };
users.push(user);
return { user };
}
const removeUser = (id) => {
const index = users.findIndex((user) => user.id === id);
if(index !== -1) return users.splice(index, 1)[0];
}
const getUser = (id) => users.find((user) => user.id === id);
const getUsersInRoom = (room) => users.filter((user) => user.room === room);
module.exports = { addUser, removeUser, getUser, getUsersInRoom };
How can i upload img file to chat app....
At first, you must create "uploads" folder to upload files.
When I run the app on local, I created folder on to D drive and changed uploader.dir from '/uploads' to "uploads";
At second, when you select DOM, you used document.elementById, this is wrong, must use ref={(el)=>this.variable=el} and change to siofu.listenOnInput(this.variable);
At third, you did define upload logic in onChange event listener, have to define in componentDidMount().

React Hooks state not updating variable in view

I'm trying to set a state, which I fetch from an API in the form of an array.
Tried this in every way possible, doesn't work.
Any ideas on how to fix this?
instance is an axios.create that creates the instance to a localhost django server which has CORS-ALLOW-CROSS-ORIGIN True
import React, { useState } from "react";
import { instance } from "../../stores/instance";
const OneList = () => {
const [one, setOne] = useState([]);
const fetchText = async () => {
const response = await instance.get(`/one/list/`);
setOne(response.data);
};
fetchText();
return (
<>
<div>Hello World.</div>
{one.forEach(o => (
<p>o.text</p>
))}
</>
);
};
export default OneList;
Do it like this,
import React, { useState } from "react";
import { instance } from "../../stores/instance";
const OneList = () => {
const [one, setOne] = useState([]);
const fetchText = async () => {
const response = await instance.get(`/one/list/`);
setOne(response.data);
};
useEffect(() => {
fetchText();
},[ any variable you want it to fetch that again ]);
return (
<>
<div>Hello World.</div>
{one.forEach(o => (
<p>o.text</p>
))}
</>
);
};
export default OneList;
This looks to be a good use case for a useEffect hook. Also, you need to await async functions within useEffect statement. The useEffect hook cannot be an async function in itself. Also, the original implementation would result in an infinite loop. The setState function would trigger a re-render which would then trigger the fetch function to fire off again. Consider the following:
import React, { useState, useEffect } from "react";
import { instance } from "../../stores/instance";
const OneList = () => {
const [one, setOne] = useState([]);
const fetchText = async () => {
const request= await instance.get(`/one/list/`);
request.then( r => setOne(r.data))
};
useEffect(() => {
(async () => {
await fetchText();
})();
}, []);
return (
<>
<div>Hello World.</div>
{one.forEach(o => (
<p>o.text</p>
))}
</>
);
};
export default OneList;
Based on you guys' advice, I came up with the below code which works fine so far.
import React, { useState, useEffect } from "react";
import { instance } from "../../stores/instance";
const OneList = () => {
const [one, setOne] = useState([]);
const [el, setEl] = useState(null);
const fetchText = async () => {
let res = await instance.get("/one/list/");
setOne(res.data);
};
useEffect(() => {
(async () => {
await fetchText();
})();
}, []);
useEffect(() => {
const handleClick = e => {
let newEl = document.createElement("input");
newEl.value = e.target.innerHTML;
newEl.id = e.target.id;
newEl.addEventListener("keypress", e => handleInput(e));
newEl.addEventListener("focusout", e => handleInput(e));
e.target.parentNode.replaceChild(newEl, e.target);
newEl.focus();
};
const handleInput = async e => {
console.log(e.type);
if (
e.target.value !== "" &&
(e.key === "Enter" || e.type === "focusout")
) {
let payload = { text: e.target.value };
try {
e.preventDefault();
let res = await instance.put(`/one/update/${e.target.id}`, payload);
let text = res.data.text;
e.target.value = text;
let newEl = document.createElement("span");
newEl.innerHTML = text;
newEl.addEventListener("click", e => handleClick(e));
newEl.id = e.target.id;
e.target.parentNode.replaceChild(newEl, e.target);
} catch (e) {
console.error(e);
}
}
};
setEl(
one.map(o => (
<p>
<span id={o.id} onClick={e => handleClick(e)}>
{o.text}
</span>
</p>
))
);
}, [one]);
return <>{el}</>;
};
export default OneList;

Resources