ReactJS: Pass object into fetch promise - reactjs

I'm kind of new react, and this quill plugin is really confusing me. I'm using react-quilljs to display an editable rich text field, which is supposed to be pre-populated with a value retrieved using fetch from my API. Seems pretty simple, right? But I'm getting the error 'quill is undefined' in the fetch callback.
import React, { useState, useEffect } from "react";
import { useQuill } from "react-quilljs";
import "quill/dist/quill.snow.css";
import "quill/dist/quill.bubble.css";
// see https://www.npmjs.com/package/react-quilljs
export default function View(props) {
const [group, setGroup] = useState([]);
const { quill, quillRef } = useQuill({});
useEffect(() => {
fetch('/api/groups/' + props.id , {
method: 'GET'
})
.then(res => res.json())
.then((data) => {
setGroup(data);
quill.setContents(JSON.parse(data));
})
.catch(console.log);
}, [quill]);
return(
<div >
<div id="descriptionInput" ref={quillRef} />
</div>
);
}
Of course I've omitted a lot of the code, but I think it should be enough to illustrate the problem. I think, basically the question is, how do I pass the quill object into the fetch promise?
I have searched for the answer but for some reason can't find anything on this.

I looked through the documents and found this:
quill.clipboard.dangerouslyPasteHTML();
I have made a working sample for you:
https://codesandbox.io/s/epic-stonebraker-itt06?file=/src/App.js:401-469

After some more inspection, it turns out useEffect is being called multiple times, and quill is not available right away (as Asher Lim notes). So adding a check if (quill) inside the fetch promise solves the problem.
Of course this means that the fetch is being done more times than necessary, which can be solved with some more refactoring.

Related

React useState hook not updating with axios call

I am aware this is a common question as I have spent the last two hours going through every answer and trying to get my state to update but nothing is working.
I am fetching text from a cms however on first load the state is undefined and my app crashes. However if I comment the line out, load the page and uncomment the line the correct values are displayed.
Here is some of the code I have tried.
The data i am hoping to get
[
{id:1},
{id:2},
{id:3},
{id:4},
]
import react, {useEffect, useState} from 'react'
import axios from 'axios'
const [carouselTitle, setCarouselTitle] = useState([])
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
await axios('api').then(
response => {
console.log(response.data)
setCarouselTitle(response.data)
console.log(carouselTitle)
})
};
return(
<h1>{carouselTitle[1].id}</h1>
)
console logging the data works fine but when i console log the state it does not work.
2ND METHOD I TRIED
useEffect(() => {
const fetchData = async () => {
const res = await axios('api');
const carouselTitleAlt = await res.data;
setCarouselTitle({ carouselTitleAlt });
console.log(carouselTitleAlt);
console.log(carouselTitle);
};
fetchData();
}, []);
Again console logging the const inside the useEffect displays the correct information but logging the state does not work.
Appreciate your responses or better ways of displaying the data.
setState is asynchronous : https://reactjs.org/docs/faq-state.html#why-doesnt-react-update-thisstate-synchronously
It means that you cannot expect to console.log the new state value the line after you called setCarouselTitle.
To log the new value, you could use another useEffect, with carouselTitle in the dependencies array, where you console.log(carouselTitle) :
useEffect(() => {
console.log(carouselTitle);
}, [carouselTitle]);
That said, your component should behave correctly, it will be refreshed when the state is updated.
In the JSX you should check that carouselTitle is not undefined (meaning that the request failed or is still pending) :
{carouselTitle && <H1>{carouselTitle[0].id}}
https://reactjs.org/docs/conditional-rendering.html#gatsby-focus-wrapper
First of all, if you pass an empty array for initial data to useState, you can't get any item in that array in here:
return(
<h1>{carouselTitle[1].id}</h1>
)
Because component returns first item of an array that has nothing. I prefer to you do it like this:
return(
<h1>{carouselTitle.length > 0 && carouselTitle[0].id}</h1>
)
And also based on this and official documentation, setState (and also setSomthing() using useState()) is asynchronous.
So state data doesn't show immediately after setting that.
You should trigger useEffect for run fetch function
useEffect(()=>{fetchData();},[carouselTitle])

React DropdownMultiselect component not updating with new options

import React, {useState, useEffect} from 'react';
import DropdownMultiselect from "react-multiselect-dropdown-bootstrap";
function App() {
const [data, setData] = useState(['test'])
const fetchApi = () => {
fetch("../api/" + ticket)
.then((response) => response.json())
.then((json) => {
let names = [];
for(var entry in json){
names.push(json[entry].print_name);
}
setData(names);
})
}
useEffect( () => {
fetchApi();},
[])
console.log('I ran')
return (
<div>
<button> {data[0]} </button>
<DropdownMultiselect options={data} />
</div>
);
}
export default App;
When I run the code above, the button updates with the name that was fetched via the fetch call, but the DropdownMultiSelect does not update its options. Am I doing it wrong, or is there something strange about the DropdownMultiSelect component that is somehow breaking things?
edit: brought the names declaration and the setData call into the second .then statement, still no joy.
I tested your code here. Looks like the problem lies within react-multiselect-dropdown-bootstrap itself and not your code.
I would suggest using a different library instead like react-select. In general it's always wiser to use third-party libraries which are being maintained frequently and have many weekly downloads.
If yet you still want to stick with react-multiselect-dropdown-bootstrap I would suggest creating an issue on their github repository.
You can try with react-multiselect-dropdown-bootstrapv1.0.4
Higher versions have some issues with the data population.
codesandbox - https://codesandbox.io/s/pensive-curran-zryxj?file=/src/App.js

Update the DOM without refreshing the page in React.js

I am fetching the data from the API (fake REST API built with json-server) in my react app. I want the app to be updated automatically without refreshing manually as any changes occur in the API. How do I do this? the code is below:
import './App.css'
import React, {useState, useEffect} from 'react'
import axios from 'axios'
export default function App() {
const [post, setPost]=useState([])
const url="http://localhost:3000/users"
useEffect(()=>{
axios
.get(url)
.then(res=>{
setPost(res.data)
})
.catch(err=>{
console.log(err)
})
},[])
const showUsers=
post.map(item=>{
return(
<div className="card" key={item.id}>
<h2>Name: {item.name}</h2>
<h3>Username: {item.username}</h3>
<p>Email:{item.email}</p>
</div>
)
})}
return (
<div className="container">
{showUsers}
</div>
)
}
NOTE I attempted to put post state variable, in the dependency array of useEffect hook, and it worked as I wanted but it started to send infinite get requests to the server. Something like that
useEffect(()=>{
axios
.get(url)
.then(res=>{
setPost(res.data)
})
.catch(err=>{
console.log(err)
})
},[post])
First, let's talk about why putting post in the useEffect dependency causes infinite requests. As stated:
You can tell React to skip applying an effect if certain values haven’t changed between re-renders.
but the problem is that every time you call the effect, you actually change post by calling setPost. this causes a re-render. while react is doing the re-render it checks whether it should run the effect again or not. since the variable post in the useEffect has changed, react decides to run the effect again. this causes the infinite loop.
Back to your main question. If I understood correctly you want to sync your application's state with the server's state regarding posts. You can achieve that by polling or websockets. in polling the client send a request to the server every few seconds to check if there has been any chaneges in the state. websockets make it possible to have a two-way connection between the server and client. the two-way connection makes it possible for the server to send information to the client at any time.
below is a very simple polling solution to your problem. I strongly encourage you to read more about the concept.
// ommited code
const [post, setPost]=useState([])
const url="http://localhost:3000/users"
const WAIT_TIME = 5000;
useEffect(() => {
const id = setInterval(() => {
axios
.get(url)
.then(res=>{
setPost(res.data);
})
.catch(err=>{
console.log(err);
})
}, WAIT_TIME);
return () => clearInterval(id);
}, [post]);
// omitted code
By default useEffect run on each update.
If you give ,[post])
It tell React to skip applying an effect if certain values (post) haven’t changed between re-renders.
If you want fetch only at mount use an ,[])
The react doc explain this very well:
Optimizing Performance by Skipping Effects

Why is fetching data in next.js with hardcoded api-key working but not with environment variable?

I followed the documentation of Next.js, so I created a file called .env.local and stored my API key inside.
When console.logging that key it gets shown in the server-console and not in the browser console. So far I understand this.
Problem: I want to fetch some data from the given URL. How can I get this done, without giving my API_KEY to the browser?
I think that topic is quite confusing for beginners (as me) and I an many other (so I think) would be overjoyed if anyone could get the confusion out of that.
Here is my code: (If I hardcode my API_KEY inside the URL it works fine)
import Haversine from '../components/Haversine'
import LangLat from '../components/LangLat'
import axios from 'axios'
import { useEffect } from 'react'
const key = process.env.API_KEY
const index = () => {
console.log(key)
const getLangLat = async () => {
try {
const response = await axios.get(
`https://geocode.search.hereapi.com/v1/geocode?q=wimpfener str 40 backnang&apiKey=${key}`
)
console.log(response.data)
} catch (err) {
console.error(err.message)
}
}
useEffect(() => {
getLangLat()
})
return (
<div>
<Haversine />
<LangLat />
</div>
)
}
export default index
You need to add NEXT_PUBLIC_ to the beginning of your environmental variable so you have access to it on the client side.
So inside of your .env.local change
API_KEY to NEXT_PUBLIC_API_KEY and do the same inside of your code.
https://nextjs.org/docs/basic-features/environment-variables#loading-environment-variables

React: I have an api fetch and the data stores into my hook state. Why can I console.log the hook object, but not access it?

I have two other useEffects in my code that fetch data, update a state hook, and allow access to all data properly in other components/same component.
I have one useEffect, however, that's grabbing data from an api fetch that uses an authorization header. I've done this before, with no issue, but on the one in question, it gets the data, updates my state hook, and I can even console.log it out in another component, but the moment I try to do something with it, it gives me an error.
UseEffect:
useEffect(() => {
const claimStudents = async() => {
const url = "http://127.0.0.1:8000/api/TaughtStudents";
const result = await axios(url, {
headers: {
Authorization: `JWT ${localStorage.getItem("token")}`,
}
})
getState.updateClaimedStudents(result.data)
}
claimStudents()
}, [])
Error:
console error
Component that is calling my state data:
function ClaimedStudents() {
const getState = useContext(UserContext)
console.log(getState.claimedStudents)
return (
<Card>
<Card.Text>
</Card.Text>
</Card>
);
}
export default ClaimedStudents;
Console log that shows my data loading, though with a delay:
Console.log showing data
I know it's something to do with promises and/or the data not delivering in the right time, but I have had little luck googling this issue for an answer.
Any insight or points in the right direction of where I can help fix this would be appreciated.
I think you need to add a request status observable, like isLoadingStudents, it should be true while fetching the data and false otherwise, then add a spinner or something in the component if the isLoadingStudents is true.
if (isLoadingStudents) {
return <Spinner/>
}
Or you can directly go with this:
Warning: You cannot do custom error handling
getState? (
getState.claimstudent.map(({student}=>{
<li> {student} </li>
}))
):(
<Loader />
)
So turns out I was a goof and did the wrong thing inside my useState() hook. Whoops me. It didn't fix my data being not accessible 100%, but I'm no longer getting the error.

Resources