How to update state value to textarea using onChange? - reactjs

Currently following a slightly older tutorial, but learning using React 18 -- trying to update the text area in a notes app
It looks like when I type, a character appears and then immediately is deleted automatically
Can anyone confirm if I might be missing a detail here?
for reference if familiar with the project at time 1:37:03 : https://www.youtube.com/watch?v=6fM3ueN9nYM&t=377s
import React, {useState, useEffect} from 'react'
import notes from '../assets/data'
import { useParams } from 'react-router-dom';
import { Link } from 'react-router-dom'
import { ReactComponent as ArrowLeft } from '../assets/arrow-left.svg'
const NotePage = ( history ) => {
const {id} = useParams();
// let note = notes.find(note => note.id===Number(id))
// console.log(id)
let [note, setNote] = useState(null)
useEffect(() => {
getNote()
}, [{id}])
let getNote = async () => {
let response = await fetch(`http://localhost:8000/notes/${id}`)
let data = await response.json()
setNote(data)
}
// let updateNote = async () => {
// await fetch(`http://localhost:8000/notes/${id}`, {
// method: 'PUT',
// headers: {
// 'Content-Type': 'application/json'
// },
// body: JSON.stringify({...note, 'updated':new Date()})
// })
// }
// let handleSubmit = () => {
// updateNote()
// history.push('/')
// }
return (
<div className="note">
<div className="note-header">
<h3>
<Link to="/">
<ArrowLeft /*onClick={handleSubmit}*/ />
</Link>
</h3>
</div>
<textarea onChange={(e) => {
setNote({...note, 'body': e.target.value}) }}
value={note?.body}>
</textarea>
</div>
)
}
export default NotePage

Your value in the useEffect dependency array is incorrect and causing getNote to be called every time you make changes in the textArea. Every time getNote is called, it's resetting the note state back to whataver is being received by getNote. Which in your case is probably a blank note
Change this :
useEffect(() => {
getNote();
}, [{ id }]);
To this:
useEffect(() => {
getNote();
}, [id]);

Related

How to dynamically render react components?

I have a website where if I press a button it fetches a post object from a database and adds it into an array. I need to somehow display all the objects in the array as react components and also to update the list every time when a new post is added.
I've been trying to use the map() method but I can't get it to display the new posts that are added when I click the button.
Main component:
import Post from './Post'
import { useState, useEffect, createElement } from 'react'
import { useCookies } from 'react-cookie';
import Axios from 'axios'
const Content = () => {
const [postArr, setPostArr] = useState([])
const getPosts = ()=>{
Axios.defaults.withCredentials = true
Axios({
method: 'get',
url: 'http://localhost:3010/api/getpost/',
headers: {'Content-Type': 'multipart/formdata' }
})
.then((response) => {
addPostToPostArray(response)
})
.catch((response) => {
console.log(response);
});
}
const addPostToPostArray = (response) => {
let imgName = response.data.imgurl.slice(68, 999999)
let sendObj = {
id:response.data.id,
posterid:response.data.posterid,
imgurl:`http://localhost:3010/images/${imgName}`,
title:response.data.title,
likes:response.data.likes,
date:response.data.date
}
postArr.push(sendObj)
/*
A fetched post will look like this:
{
id:123, posterid:321, imgurl:`http://localhost:3010/images/333.png`,
title:'best title', likes:444, date:111111
}
*/
}
return (
<div>
{postArr.map((e) => {
return <Post post={e}/>
})}
<button onClick={getPosts}>load post</button>
</div>
);
}
export default Content;
Post component:
const Post = (props) => {
const post = props.post
return (
<div className='post-frame'>
<h1>{post.title}</h1>
<div className="image-frame">
<img src={post.imgurl}></img>
</div>
<p>{post.likes}</p>
<p>{post.posterid}</p>
</div>
);
}
export default Post;
To update the state of the component you need to call that setPostArr function with the updated array. Without that the state of the component never get's updated.
Here's an example
const Content = () => {
const [postArr, setPostArr] = useState([])
const getPosts = () => {
...
}
const addPostToPostArray = (response) => {
let sendObj = {
...
}
// ~ This part here
setPostArr([...postArr, sendObj])
// ~ Instead of
// postArr.push(sendObj)
}
return ...
}

How to display data from Mongodb in React using Axios

I'm facing difficulty displaying data in React - Here is my code:
import Axios from 'axios';
import { useNavigate } from 'react-router';
export default function ProductCatalog() {
let navigate = useNavigate();
function addProduct() {
navigate('/adding')
}
const [products, setProducts] = useState([{}])
useEffect(() => {
const axiosProd = async () => {
const response = await Axios('http://localhost:3001/getProducts');
setProducts(response.data)
};
axiosProd();
}, []);
const useProducts = products.map((product)=>{
return <div>
<h1>{product.name}</h1>
</div>
})
return(
<>
<button className = "button" onClick={addProduct}>Add New Product</button>
<br></br>
{useProducts}
</>
)
}
I know data is coming in as JSON Objects as when i follow the link of http://localhost:3001/getProducts, I see my data. What am i doing wrong?
You should make a function then outside of the function call the use effect.
To do a get request using axios use axios.get(api)
For example:
// Get All Shoes
const getShoes = () => {
axios.get('/shoes')
.then(res => setShoes(res.data))
.catch(err => console.log(err));
}
Then
useEffect(() => {
getShoes();
}, [])

Why doesn't the axios response get saved in useState variable

I've built a random photo displaying feature in react.
the console says that the response is valid and it works,
but the page breaks when I return data.
Where is the issue?
Thanks in advance!
import React from 'react'
import { useEffect, useState } from 'react'
import axios from 'axios'
function RandomPhoto() {
const url = `https://api.unsplash.com/photos/random/?client_id=${process.env.REACT_APP_UNSPLASH_KEY}`
const [data, setData] = useState()
const getPhoto = () => {
axios.get(url)
.then(response => {
setData(response.data)
console.log(response.data) // <------- works
})
.catch(error => {
console.log(error)
})
}
useEffect(() => {
getPhoto()
},[])
console.log("XX" + data) // <---------- doesn't work, and following return() neither
return (
<div>
<img href={data.urls.regular} alt={data.alt_description}/>
<p>Photo by {data.username} {data.name} from {data.location} - found on unsplash</p>
</div>
)
}
export default RandomPhoto
I modified your code a bit, and it's working. I made it as an async function and changed the path of JSON object keys.
Please note the location data sometimes returns as null. So you have to render it conditionally.
import React from 'react';
import { useEffect, useState } from 'react';
import axios from 'axios';
const RandomPhoto = () => {
const url = `https://api.unsplash.com/photos/random/?client_id=${process.env.REACT_APP_UNSPLASH_KEY}`;
const [imageData, setImageData] = useState('');
const getPhoto = async () => {
await axios
.get(url)
.then((response) => {
setImageData(response.data);
})
.catch((error) => {
console.log(error);
});
};
useEffect(() => {
getPhoto();
}, []);
return (
<div>
<p>Hello</p>
<img src={imageData.urls?.regular} />
<p>
Photo by {imageData?.user?.username} {imageData?.user?.name} from{' '}
{imageData?.location?.country} - found on unsplash
</p>
</div>
);
};
export default RandomPhoto;

Fetch runs in a loop

I am fetching data in my react component
import React, { useState, useEffect } from 'react';
import { fetchBookData } from './bookData';
import "./App.css";
export default function Books ({ books }) {
const [payload, setPayload] = useState(null)
fetchBookData(books).then((payload) => setPayload(payload));
return (
<div className="App">
<h1>Hello</h1>
</div>
);
}
Here is the fetch function itself
const dev = process.env.NODE_ENV !== 'production';
const server = dev ? 'http://localhost:3001' : 'https://your_deployment.server.com';
// later definable for developement, test, production
export const fetchBookData = (books) => {
const options = {
method: `GET`,
headers: {
accept: 'application/json',
},
};
return fetch(`${server}/books`, options)
.then((response) => {
if(response.ok){
return response.json()
}
throw new Error('Api is not available')
})
.catch(error => {
console.error('Error fetching data in book data: ', error)
})
}
But when I start the server fetch runs in a loop, component making endless get requests to the server. I tried to wrap it in a useEffect, but didn't work. Fetch should run once on load
If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument. More
example (codesandbox)
export default function App({books}) {
const [payload, setPayload] = useState(null);
useEffect(() => {
fetchBookData(books).then((payload) => setPayload(payload));
}, [books]);
if (!payload) {
return <h1>Loading...</h1>;
}
return (
<div className="App">
<h1>{payload.title}</h1>
</div>
);
}

window is not defined in next js

some help here
I am trying to use react-wysiwyg-editor in my next js app, but it prints an error of window is undefined
import React, { useState } from "react";
import { Editor } from "react-draft-wysiwyg";
import { EditorState } from "draft-js";
import "../node_modules/react-draft-wysiwyg/dist/react-draft-wysiwyg.css";
import classes from "./temps.module.css";
const EmailTemps = () => {
const [editorState, setEditorState] = useState(EditorState.createEmpty());
const handleClick = async () => {
const response = await fetch("/api/sendMail", {
method: "POST",
body: JSON.stringify({ editorState }),
headers: {
"Content-Type": "application/json",
},
});
const data = await response.json();
console.log(data.message);
};
if (typeof window !== undefined) {
<div className={classes.nnn}>
<Editor editorState={editorState} onEditorStateChange={setEditorState} />
<button onClick={handleClick} className={classes.but}>
send email
</button>
</div>;
}
};
export default EmailTemps;
i have tried working around this by
if (typeof window !== undefined) {...}
but the error is persisting, i am not sure if i am just not doing it the right way
also, i have tried the next/dynamic, but i couldnt dynamically import something from nodemodules
Right now I know that all i am left with is to use useEffect, which i wouldnt want to, if i have a better alternative. my inquiry, is there any better way i can achieve this ?
I think your main problem is not from your own window object but it's from react-draft-wysiwyg which only supports on the client-side.
To fix it, you need to use dynamic import
import dynamic from 'next/dynamic';
const Editor = dynamic(
() => import('react-draft-wysiwyg').then(mod => mod.Editor),
{ ssr: false }
)
The 2nd problem is your window check should be against a string "undefined" instead of undefined
typeof window !== "undefined"
The 3rd problem is you never return your component elements
if (typeof window === "undefined") {
return null //return nothing on the server-side
}
//return only on the client-side
return <div className={classes.nnn}>
<Editor editorState={editorState} onEditorStateChange={setEditorState} />
<button onClick={handleClick} className={classes.but}>
send email
</button>
</div>
The full implementation
import React, { useState } from "react";
import dynamic from 'next/dynamic';
const Editor = dynamic(
() => import('react-draft-wysiwyg').then(mod => mod.Editor),
{ ssr: false }
)
import { EditorState } from "draft-js";
import "../node_modules/react-draft-wysiwyg/dist/react-draft-wysiwyg.css";
import classes from "./temps.module.css";
const EmailTemps = () => {
const [editorState, setEditorState] = useState(EditorState.createEmpty());
const handleClick = async () => {
const response = await fetch("/api/sendMail", {
method: "POST",
body: JSON.stringify({ editorState }),
headers: {
"Content-Type": "application/json",
},
});
const data = await response.json();
console.log(data.message);
};
if (typeof window === "undefined") {
return null //return nothing on the server-side
}
//return only on the client-side
return <div className={classes.nnn}>
<Editor editorState={editorState} onEditorStateChange={setEditorState} />
<button onClick={handleClick} className={classes.but}>
send email
</button>
</div>
}
};
export default EmailTemps;
I have solved this problem by re-using next/dynamic, in the component where i am using EmailTemps like below
import dynamic from 'next/dynamic'
import styles from '../styles/Home.module.css'
export default function Home() {
const EmailEditor = dynamic( ()=> {
return import('../comps/emailtemps');
}, { ssr: false } );
return (
<div className={styles.container}>
<EmailEditor />
</div>
)
}
and it just worked, thank you guys, however in the client console, it prints this error
next-dev.js?3515:32 Warning: Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to `this.state` directly or define a `state = {};` class property with the desired state in the r component.
kindly help how i can go about this now

Resources