SaveData is not output and instanceRef null - javascript

I have a problem with react-editor-js library. I'm trying to get data from it, but it doesn't output and when I check instanceRef it's null. Here is the code.
const Create = () => {
const instanceRef = React.useRef(null);
let data = { '1': 'test' }
async function handleSave() {
const savedData = await instanceRef.current.save()
console.log(savedData)
}
return (
<div>
<Header Name='Создание'/>
<Menu/>
<div className="editor">
<ReactEditorJS
instanceRef={(instance) => (instanceRef.current = instance)}
tools={EDITOR_JS_TOOLS}
data={data}
onChange={() => handleSave}
/>
</div>
</div>
);
};
If I use
onChage={() => handleSave}
I get this error.
Uncaught (in promise) TypeError: Cannot read properties of null (reading 'save')
Thanks for your help!

As per ReactEditorJS docs - you should use onInitialize to save your instance on initialize.
So in your case try this -
const Create = () => {
const instanceRef = React.useRef(null);
let data = { '1': 'test' }
async function handleSave() {
const savedData = await instanceRef.current.save()
console.log(savedData)
}
return (
<div>
<Header Name='Создание'/>
<Menu/>
<div className="editor">
<ReactEditorJS
//changed instanceRef to onInitialize
onInitialize={(instance) => (instanceRef.current = instance)}
tools={EDITOR_JS_TOOLS}
data={data}
//need to add '()' to call handleSave
// or you can change it to onChange={handleSave}
onChange={() => handleSave()}
/>
</div>
</div>
);
};
References -
How to access editor-js instance?
Note - I have not tried this solution but by reading the documentation I have added this answer, let me know if it does not work.

Related

Why result of api not storing in localstorage after login?

Actually I'm a beginner in react. I'm having problem with getting data from api immediately after login. It is not getting stored in localstorage.
const Ticket = () => {
const [ticket, setTicket] = useState('');
useEffect(() => {
getTicket();
}, []);
const getTicket = async () => {
let result = await fetch('http://127.0.0.1:5000/');
result = await result.json();
setTicket(result);
localStorage.setItem('answers', JSON.stringify(result['answers']));
}
var answers = localStorage.getItem('answers');
answers = JSON.parse(answers);
const [cell, setCell] = useState(answers);
const handleClick = event => {
event.currentTarget.classList.add('striked');
}
return (
<div>
<div className='board'>
<div className='tambola-ticket'>
{cell.map((cell, number) => (
<div className={'tambola-ticket-cell '} onClick={handleClick}>
<h4>{cell}</h4>
</div>
))}
</div>
</div>
</div>
);
}
export default Ticket;
Uncaught TypeError: Cannot read properties of null (reading 'map')
As I was writing code, I was able to get data on console and display it also. But after relogin it is throwing error.
I tried to recheck it step-by-step it is working, but the whole code simultaneously not storing data when relogedin.

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'data'), map is not working?

I have a API this is working fine in console. but when I'm using map this is not working giving error.
Getting this error:-
My Code:-
function GetEgyptStates() {
const [egyptData, setEgyptData] = React.useState(null);
React.useEffect(() => {
axios.get('http://localhost:3000/states-and-districts.json').then((response) => {
setEgyptData(response.data);
console.log(response.data.Egypt.Assuit);
});
}, []);
if (!egyptData) return null;
return (
<div>
{egyptData.response.data.Egypt.Assuit.map((item) => (
<li>{item}</li>
))}
</div>
);
}
ThankYou for your Support!
function GetEgyptStates() {
const [egyptData, setEgyptData] = React.useState(null);
React.useEffect(() => {
axios.get('http://localhost:3000/states-and-districts.json').then((response) => {
setEgyptData(response.data);
console.log(response.data.Egypt.Assuit);
});
}, []);
if (!egyptData) return null;
return (
<div>
{egyptData.Egypt.Assuit.map((item) => (
<li>{item}</li>
))}
</div>
);
}
Try this. You are already setting the response data using response.data in state therefor you don't need to use response.data again
Further, if this doesn't work, then please post a screenshot of api response from console in question.
Thanks,

Make React JS await for a async func to complete before running

I'm trying to make react not load until after an axios get requests finishes. I'm pretty rough on react all around, so sorry in advance.
I'm getting an array of objects
const { dogBreedsTest } = useApplicationData()
And I need it to be the default value of one of my states
const [dogBreeds, updateDogBreeds] = useState(dogBreedsTest);
However, I'm getting an error that my value is coming up as null on the first iteration of my app starting. How can I ensure that my value has completed my request before my app tries to use it?
Here is how I am getting the data for useApplicationData()
const [dogBreedsTest, setDogBreeds] = useState(null);
const getDogBreeds = async () => {
try{
const { data } = await axios.get('https://dog.ceo/api/breeds/list/all')
if(data) {
const newDogList = generateDogsArray(data['message'])
const generatedDogs = selectedDogs(newDogList)
setDogBreeds(generatedDogs)
}
} catch(err) {
console.log(err);
}
}
useEffect(() => {
getDogBreeds()
}, []);
return {
dogBreedsTest,
setDogBreeds
}
And I am importing into my app and using:
import useApplicationData from "./hooks/useApplicationData";
const { dogBreedsTest } = useApplicationData()
const [dogBreeds, updateDogBreeds] = useState(dogBreedsTest[0]);
const [breedList1, updateBreedList1] = useState(dogBreedsTest[0])
function handleOnDragEnd(result) {
if (!result.destination) return;
const items = Array.from(dogBreeds);
const [reorderedItem] = items.splice(result.source.index, 1);
items.splice(result.destination.index, 0, reorderedItem);
for (const [index, item] of items.entries()) {
item['rank'] = index + 1
}
updateDogBreeds(dogBreedsTest[0]);
updateBreedList1(dogBreedsTest[0])
}
return (
<div className="flex-container">
<div className="App-header">
<h1>Dog Breeds 1</h1>
<DragDropContext onDragEnd={handleOnDragEnd}>
<Droppable droppableId="characters">
{(provided) => (
<ul className="dogBreeds" {...provided.droppableProps} ref={provided.innerRef}>
{breedList1?.map(({id, name, rank}, index) => {
return (
<Draggable key={id} draggableId={id} index={index}>
{(provided) => (
<li ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
<p>
#{rank}: { name }
</p>
</li>
)}
</Draggable>
);
})}
{provided.placeholder}
</ul>
)}
</Droppable>
</DragDropContext>
</div>
)
error: TypeError: Cannot read property 'map' of null
(I am mapping the data later in the program)
const getDogBreeds = async () => {
try {
const { data } = await axios.get('https://dog.ceo/api/breeds/list/all')
if(data) {
const newDogList = generateDogsArray(data['message'])
const generatedDogs = selectedDogs(newDogList)
setDogBreeds(generatedDogs)
}
} catch(err) {
console.log(err);
}
}
useEffect(() => {
getDogBreeds() // -> you are not awaiting this
}, []);
Do this instead
useEffect(() => {
axios.get('https://dog.ceo/api/breeds/list/all')
.then(res => {
const newDogList = generateDogsArray(res.data['message']);
const generatedDogs = selectedDogs(newDogList);
setDogBreeds(generatedDogs);
})
.catch(err => console.log(err));
}, []);
I know this looks awful, but I don't think you should use async/await inside useEffect
Use this in your application
useEffect will update whenever dogBreedsTest is changed. In order to make it work, start with null values and update them to the correct initial values once your async operation is finished.
const { dogBreedsTest } = useApplicationData();
const [dogBreeds, updateDogBreeds] = useState(null);
const [breedList1, updateBreedList1] = useState(null);
useEffect(() => {
updateDogBreeds(dogBreedsTest[0]);
updateBreedList1(dogBreedsTest[0]);
}, [dogBreedsTest]);
The problem is, that react first render and then run useEffect(), so if you don't want to render nothing before the axios, you need to tell to react, that the first render is null.
Where is your map function, to see the code? to show you it?.
I suppose that your data first is null. So you can use something like.
if(!data) return null
2nd Option:
In your map try this:
{breedList1 === null
? null
: breedList1.map(({id, name, rank}, index) => (
<Draggable
key={id} draggableId={id} index={index}>
{(provided) => (
<li ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
<p>
#{rank}: { name }
</p>
</li>
)}
</Draggable> ))}
You have null, because your axios is async and react try to render before any effect. So if you say to react that the list is null, react will render and load the data from the api in the second time.
Option 1 use the optional chaining operator
dogBreedsTest?.map()
Option 2 check in the return if dogBreedsTest is an array
retrun (<>
{Array.isArray(dogBreedsTest) && dogBreedsTest.map()}
</>)
Option 3 return early
if (!Array.isArray(dogBreedsTest)) return null
retrun (<>
{dogBreedsTest.map()}
</>)
Option 4 set initial state
const [dogBreedsTest, setDogBreeds] = useState([]);
You could also add a loading state and add a loading spinner or something like that:
const [dogBreedsTest, setDogBreeds] = useState(null);
const [loading, setLoading] = useState(true)
const getDogBreeds = async () => {
setLoading(true)
try{
const { data } = await axios.get('https://dog.ceo/api/breeds/list/all')
if(data) {
const newDogList = generateDogsArray(data['message'])
const generatedDogs = selectedDogs(newDogList)
setDogBreeds(generatedDogs)
}
} catch(err) {
console.log(err);
}
setLoading(false)
}
useEffect(() => {
getDogBreeds()
}, []);
return {
dogBreedsTest,
loading,
setDogBreeds
}
Edit
Try to use a useEffect hook to update the states when dogBreedsTest got set.
const { dogBreedsTest } = useApplicationData()
const [dogBreeds, updateDogBreeds] = useState(dogBreedsTest?.[0] ?? []);
const [breedList1, updateBreedList1] = useState(dogBreedsTest?.[0] ?? [])
useEffect(() => {
updateDogBreeds(dogBreedsTest?.[0] ?? [])
updateBreedList1(dogBreedsTest?.[0] ?? [])
}, [dogBreedsTest])

Problem using map method with react (posts.map is not a function)

i'm having an error while trying to map an array (code below). The error i'm receiving is : Uncaught (in promise) TypeError: posts.map is not a function.
I have compared it to one of my old project, and it worked perfectly on the older one. Thanks in advance
const MainContent = () => {
const openModal = () => {
const modal = document.querySelector(".modal");
modal.style.display = "block";
};
const [posts, setPosts] = useState([]);
const getPosts = async () => {
const response = await fetch('http://localhost:5000/api/post/');
const posts = await response.json();
setPosts(posts);
console.log(posts)
}
useEffect(() => {
getPosts();
}, []);
return (
<section className="main-content-section">
<button className="button post-btn" onClick={openModal}>
Créer un nouveau post
</button>
<ModalPost style={{ display: "none" }} />
<div className="post-container">
{posts.map((post) => {
const { id, title, content, author } = post;
return (
<div className="post-content" key={id}>
<div className="post-content__user-info">
<div className="post-content__photo-container">
</div>
<div className="post-content__name">
<h3>{author}</h3>
</div>
</div>
<div className="post-content__content">
<h3>{title}</h3>
<p>{content}</p>
</div>
<div className="post-content__like-box">
<BiHeart className="like-heart like" size={26} />
<RiDislikeLine className="dislike-heart dislike" size={26} />
</div>
</div>
);
})}
</div>
</section>
);
};
Just add if statement before mapping post array. The reason is that posts array does not get data immediately causing map function to fail. if condition will wait until posts array actually contains data
{
(posts && posts.length > 0) ?
(posts.map((post) => {})
: (<p> loading... </p>)
}
const getPosts = async () => {
const response = await fetch('http://localhost:5000/api/post/');
const posts = await response.json();
setPosts(posts);
console.log(posts)
}
Here, the result of response.json() is going to be an object, and not an array. Probably, you have to look inside the object and find the posts array.

How can I map images in React from firebase storage?

Here I am trying to get productList from MySQL database and for each product object I am assigning new property - imageURL via getImages(). When I log productList to console, there is property imageURL with correct url. Problem is, when I try to map it, it shows nothing. Why?
const storageRef = firebase.storage().ref("/assets")
const [productList, setProductList] = useState([])
useEffect(() => {
Axios.get("http://localhost:3001/product/get").then((response) => {
setProductList(response.data)
})
}, [])
useEffect(() => {
getImages(productList)
}, [productList])
const getImages = (array) => {
array.forEach((item) => {
storageRef.child(`${item.bannerImage}`).getDownloadURL().then((url) => {
item.imageURL = url
})
})
}
My map function:
{productList.map((val) => {
return (
<div key={val.id} className="product">
<div className="item">
<h1>Product title: {val.title}</h1>
<h2>Price: {val.price}</h2>
<h2>Quantity: {val.quantity}</h2>
<h2>IMAGE: {val.imageURL}</h2>
</div>
</div>
)
})}
Problems:
You are not setting productList back in getImages function. You are just iterating over array.
getDownloadURL is a async function, you should not use it inside loop. The best way to do this is through a recursive function. But you can also do this as below:
Solution
Your getImage function
const getImage = async (bannerImage) => {
const url = await storageRef.child(bannerImage).getDownloadURL();
return url;
}
then your map function
{productList.map((val) => {
return (
<div key={val.id} className="product">
<div className="item">
<h1>Product title: {val.title}</h1>
<h2>Price: {val.price}</h2>
<h2>Quantity: {val.quantity}</h2>
<h2>IMAGE: {getImage(val.bannerImage)}</h2>
</div>
</div>
)
})}
I would suggest you create another small component for your image rendering and handle async for getDownloadURL behaviour inside that component
function ProductImage({bannerImage}) {
const [imageUrl, setImageUrl] = useState('')
useEffect(() => {
async function getImage(bannerImage) {
const url = await bannerImage.getDownloadURL()
setImageUrl(url)
}
getImage(bannerImage)
}, [bannerImage])
return imageUrl ? <h2>IMAGE: {imageUrl}</h2> : '...Loading'
}
And use this component in your main component
{productList.map((val) => {
return (
<div key={val.id} className="product">
<div className="item">
<h1>Product title: {val.title}</h1>
<h2>Price: {val.price}</h2>
<h2>Quantity: {val.quantity}</h2>
<ProductImage bannerImage={val.bannerImage} />
</div>
</div>
)
})}

Categories