How do I store an image in react useState - reactjs

I'm trying to add a form in which users can add text and images which is then going to be stored on the mongodb database. As of right now, I'm trying to add a section in the form where users can add images. I'm trying to figure out a way to store an image that the user uploads and send it to the backend (I'm using MERN stack).
This is my code that deals with the form, in terms of getting user input and sending it to the backend:
import { useState } from "react";
import { adminSDGOptions } from "./AdminCategoryLists";
import { adminAssingmentOptions } from "./AdminCategoryLists";
import { keywordsOptions } from "../FilterComponents/CategoryArrays/KeywordsOptions";
import Multiselect from "multiselect-react-dropdown"
import Select from 'react-select';
const ProjectAdminForm = () => {
// Adding basic info
const [sdg, setSDG] = useState('')
const [goal, setGoal] = useState('')
const [orginization, setOrginization] = useState('')
const [source, setSource] = useState('')
const [location, setLocation] = useState('')
const [published, setPublished] = useState('')
const [website_url, setWebsiteURL] = useState('')
const [assignment_type, setAssignmentType] = useState('')
const [sharepoint_link, setSharepointLink] = useState('')
const [statement, setStatement] = useState('')
const [preview_img, setPreviewImg] = useState([])
const [error, setError] = useState(null)
// Adding keywords
const [keywords, setKeywords] = useState([]);
const handleSubmit = async (e) => {
e.preventDefault() // Prevents refresh of page from happening
console.log('button clicked')
const project = {sdg, goal, orginization, source, location, published, website_url, assignment_type, keywords, sharepoint_link, statement, preview_img}
console.log(project)
// Sending form response to backend
const response = await fetch('/api/projects', {
method: 'POST',
body: JSON.stringify(project),
headers: {
'Content-Type': 'application/json'
}
})
const json = await response.json
// Checking for error
if (!response.ok) {
setError(json.error)
}
if (response.ok) {
// Reset form inputs back to empty string
setSDG('')
setGoal('')
setOrginization('')
setSource('')
setLocation('')
setPublished('')
setWebsiteURL('')
setAssignmentType('')
setKeywords([])
setSharepointLink('')
setStatement('')
setError(null)
alert('Project added!')
console.log('new project added', json)
}
}
return (
<form className="create project-form" onSubmit={handleSubmit}>
<h2 style={{"textAlign": "center"}}>Add a New Project</h2>
<hr></hr>
<label>Sustainable Development Goal:</label>
<Select
className="basic-single"
classNamePrefix="select"
placeholder="Select"
name="color"
options={adminSDGOptions}
onChange={(selection) => setSDG(selection.value)}
required
/>
<label>Description:</label>
<input
type="text"
onChange={(e) => setGoal(e.target.value)}
value={goal}
required
/>
<label>OPTIONAL - Organization:</label>
<input
type="text"
onChange={(e) => setOrginization(e.target.value)}
value={orginization}
/>
<label>OPTIONAL - Source:</label>
<input
type="text"
onChange={(e) => setSource(e.target.value)}
value={source}
/>
<label>OPTIONAL - Location:</label>
<input
type="text"
onChange={(e) => setLocation(e.target.value)}
value={location}
/>
<label>Published (YEAR ONLY):</label>
<input
type="text"
onChange={(e) => setPublished(e.target.value)}
value={published}
required
/>
<label>OPTIONAL - Website URL:</label>
<input
type="text"
onChange={(e) => setWebsiteURL(e.target.value)}
value={website_url}
/>
<label>Assignment Type:</label>
<Select
className="basic-single"
classNamePrefix="select"
placeholder="Select"
name="color"
options={adminAssingmentOptions}
onChange={(selection) => setAssignmentType(selection.value)}
required
/>
<hr></hr>
<label>Enter Keyword(s):</label>
<Multiselect
className="multiselect-admin"
isObject={false}
onRemove={(selection) => setKeywords(selection)}
onSelect={(selection) => setKeywords(selection)}
options={keywordsOptions}
required
/>
<hr></hr>
<label>OPTIONAL - Statement (ONLY Assessment Ideas and Discussion Topics):</label>
<input
type="text"
onChange={(e) => setStatement(e.target.value)}
value={statement}
/>
<label>OPTIONAL - Qualtrics Link (ONLY Mini Case Study):</label>
<input
type="text"
onChange={(e) => setSharepointLink(e.target.value)}
value={sharepoint_link}
/>
// THIS IS THE SECTION I'M TRYING TO ADD AND AM NOT SURE HOW TO GO ABOUT DOING SO
<label>OPTIONAL - Preview image:</label>
<input
type="file"
name="preview_img"
accept="image/*"
onChange={(e) => setPreviewImg(e.target.value)}
/>
<div className="add-proj">
<button>Add Project</button>
</div>
{error && <div className="error">{error}</div>}
</form>
)
}
export default ProjectAdminForm
The main thing I'm trying to get working is the preview_img stuff, I'm able to ask the user for an image through <input/> but then when I store itin my useState which is set up like this const [preview_img, setPreviewImg] = useState([]), it doesn't save the actual image, rather a string which looks like this:
preview_img: "C:\\fakepath\\banner2 (2).jpg"
I'm not sure how to save the actual image and then send it to the backend in my handleSubmit function since it appears I can't just include the state name (preview_img) in the object I'm sending to the backend.

You can reach and upload the image in onChange function. If you want to use it by accessing local url, you can create and store its local url in a state.
const [imageURL,setImageURL] = useState()
onChange={(event) => {
let file = event.target.files[0]
yourUploadFunction(file, url)
setImageURL(URL.createObjectURL(file))
}}

Related

Onchange in input field is not working while editing a form

I am developing a small application in react, in which I have an edit option. On clicking the edit button, it will load the existing data and allows the user to edit any of the fields and submit.
Fetching the data and loading it in a form are working fine, but when I edit a textbox, the value changes to the existing fetched value, and it is not allowing me to hold the edited value.
Please note, the problem is with editing the input in a form not in submitting. Below is the edit component that I am using.
mport { useState, useEffect } from 'react';
import { json, Link } from 'react-router-dom';
import { useParams } from 'react-router-dom';
const EditTask = ({ onEdit }) => {
const [text, setText] = useState('');
const [day, setDay] = useState('');
const [reminder, setReminder] = useState(false);
const params = useParams();
useEffect(() => {
fetchTask();
});
const fetchTask = async () => {
const res = await fetch(`http://localhost:5000/tasks/${params.id}`);
const data = await res.json();
setText(data.text);
setDay(data.day);
setReminder(data.reminder);
};
const onSubmit = async (e) => {
e.preventdefault();
if (!text) {
alert('Please enter task name');
return;
}
onEdit({ text, day, reminder });
setText('');
setDay('');
setReminder(false);
};
const handleChange = ({ target }) => {
console.log(target.value); // displaying the input value
setText(target.value); // changes to existing value not the one I entered
};
return (
<form className="add-form" onSubmit={onSubmit}>
<div className="form-control">
<label>Task</label>
<input
id="AddTask"
type="text"
placeholder="Add Task"
value={text}
onChange={handleChange}
/>
</div>
<div className="form-control">
<label>Date & Time</label>
<input
id="Date"
type="text"
placeholder="Date & Time"
value={day}
onChange={(e) => setDay(e.target.value)}
/>
</div>
<div className="form-control form-control-check">
<label>Set Reminder</label>
<input
id="Reminder"
type="checkbox"
checked={reminder}
value={reminder}
onChange={(e) => setReminder(e.currentTarget.checked)}
/>
</div>
<input className="btn btn-block" type="submit" value="Save Task" />
<Link to="/">Home</Link>
</form>
);
};
export default EditTask;
Can someone explain what I am missing here? Happy to share other information if needed.
Expecting the input fields to get the value entered and submitting.
You missed adding dependency to useEffect
Yours
useEffect(() => {
fetchTask()
}
)
Should be changed
useEffect(()=>{
fetchTask()
}, [])
becasue of this, fetchTask is occured when view is re-rendered.

update text to API using ReactQuill texteditor

Im trying to update my text in a reactquill texteditor. I also have a input where I can change the title, and it works fine. but when Im trying to set the Text from the text editor it never change.
Im getting the title and text from my update API and it works fine in postman so Im pretty sure its the text editor thats not working with me.
This is what I got so far:
function UpdateDoc() {
const routing = useNavigate();
const params = useParams();
const [name, setName] = useState()
const [text, setText] = useState()
//const quill = useRef();
useEffect(() => {
getDocDetails();
}, [])
const getDocDetails = async () => {
let res = await docModel.getOneDoc(params.id)
setName(res.name)
setText(res.text)
}
const updateDocDetails = async () => {
let res = await docModel.updateOneDoc({ name, text }, params.id)
console.log(res)
routing("/show")
}
return (
<div>
<h2>Update Doc</h2>
<>
<div className='toolbar'>
<button className="button-5" type="button" onClick={updateDocDetails} >
Update
</button>
</div>
<div className='createcontainer'>
<label>Name of Document: </label>
<input
type="text"
placeholder="Add Name of document"
value={name}
onChange={(e) => { setName(e.target.value) }}
name="name"
className="name-text"
/>
<ReactQuill
placeholder="Add text to document"
value={text}
theme="snow"
name="text"
//value="editor.getContents()"
onEditorChange={(e) => { setText(e.target.ref) }} // I FEEL LIKE IS THIS BIT THAT DOS NOT WORK FOR ME
//ref={quill}
/>
</div>
</>
</div >
)
}
export default UpdateDoc

reactjs "TypeError: Cannot read properties of undefined (reading 'params')"

I'am new using reactjs and looks like I am following the old tutorial with old version of react router. So the objectives is when I want to edit the user detail, the form must be filled with data of previous user before update. I've already comunicate with the backend using axios and it worked fine, but the problem is in "id = props.match.params.id". and here is my code:
UserEdit.tsx
import axios from "axios";
import React, { SyntheticEvent, useEffect, useState } from "react";
import { Navigate } from "react-router-dom";
import Wrapper from "../../components/Wrapper";
import { Role } from "../../models/role";
const UserEdit = (props: any) => {
const [first_name, setFirstName] = useState('');
const [last_name, setLastName] = useState('');
const [email, setEmail] = useState('');
const [role_id, setRoleId] = useState('');
const [roles, setRoles] = useState([]);
const [redirect, setRedirect] = useState(false);
let id: number;
useEffect(() => {
(
async () => {
const response = await axios.get('roles');
setRoles(response.data);
id = props.match.params.id;
const {data} = await axios.get(`users/${id}`);
setFirstName(data.first_name);
setLastName(data.last_name);
setEmail(data.email);
setRoleId(data.role_id);
}
)()
}, []);
const submit = async (e: SyntheticEvent) => {
e.preventDefault();
await axios.put('users', {
first_name,
last_name,
email,
role_id
});
setRedirect(true)
}
if(redirect) {
return <Navigate to="/users"/>
}
return (
<Wrapper>
<form onSubmit={submit}>
<h1 className="h3 mb-3 fw-normal">Edit user</h1>
<div className="form-floating">
<input className="form-control" placeholder="First Name" defaultValue={first_name} onChange={e => setFirstName(e.target.value)} required/>
<label htmlFor="floatingInput">First Name</label>
</div>
<div className="form-floating">
<input className="form-control" placeholder="Last Name" defaultValue={last_name} onChange={e => setLastName(e.target.value)} required/>
<label htmlFor="floatingInput">Last Name</label>
</div>
<div className="form-floating">
<input type="email" className="form-control" placeholder="Email Address" defaultValue={email} onChange={e => setEmail(e.target.value)} required/>
<label htmlFor="floatingInput">Email Address</label>
</div>
<div className="form-floating">
<select className="form-control" id="floatingRole" placeholder="Role" value={role_id} onChange={e => setRoleId(e.target.value)} required>
{roles.map((r: Role) => {
return (
<option key={r.id} value={r.id}>{r.name}</option>
)
})}
</select>
<label htmlFor="floatingRole">Role</label>
</div>
<button className="w-100 btn btn-lg btn-primary" type="submit">Save</button>
</form>
</Wrapper>
);
};
export default UserEdit;
As you can see in this image below, the data isn't show up and get the error message like this
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'params')
I really appreciate if anyone wants to help me solve this problem so I can learn further. Thankyou
In react-router-dom#6 there are no longer "route props", i.e. no location, history (well, now navigate), and no match.
Use the useParams hook to access the route path parameters.
...
import { Navigate, useParams } from "react-router-dom";
...
const UserEdit = (props: any) => {
const { id } = useParams();
...
useEffect(() => {
(async () => {
const response = await axios.get('roles');
setRoles(response.data);
const { data } = await axios.get(`users/${id}`);
setFirstName(data.first_name);
setLastName(data.last_name);
setEmail(data.email);
setRoleId(data.role_id);
})();
}, []); // <-- add `id` to dependency array if you need to be responsive
...
return (
...
);
};
It seems like your params not pass correctly .
Maybe you need to get URL parameters by using useParams
like document (https://v5.reactrouter.com/web/example/url-params)
, or check the Router component path format is correct in your React Router version .
In React Router ^v5.3.0 :
<Route path="users/:id/edit" />

How do I write an edit-file for mixed data in React

I want to update an article with image.
I am using a mern-stack with redux-toolkit.
I tried to bring the data from the database into the inputfields by using useEffect() and that works, and currently I get no error anymore, but when I press update, I get an alert:
localhost:3000 includes undefined
and then the pending of my updatefunction stops. So nothing is updated. The preview of my picture works.
Can someone of the experienced people tell me the right way, to write this edit file?
Here is my react-component(current state):
const MainnewsEdit = () => {
const {mainnews, isLoading, isError, message} = useSelector((state)=>state.mainnews);
const dispatch = useDispatch();
const {id} = useParams();
useEffect(()=>{
if(isError){
window.alert(message);
}
dispatch(getMainNews(id))
}, [isError, message, dispatch,id]);
const savedData = {
ressort:"",
theme:"",
title:"",
content:"",
}
const [data, setData] = useState(savedData);
useEffect(()=>{
if(mainnews){
setData({
ressort: mainnews.ressort,
theme: mainnews.theme,
title: mainnews.title,
content:mainnews.content,
})
}
}, [mainnews]);
//img
const [fileData, setFileData] = useState({img:""})
const {img} = fileData;
const fileInput = useRef(img);
const fileChange = (e)=>{
const file = fileInput.current.files[0];
setFileData(file);
handlePreview(file)
}
console.log(fileData);
const [preview, setPreview] = useState(false)
const handlePreview = (file)=>{
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = ()=>{
setPreview(reader.result);
}
}
const {ressort, theme, title, content} = data;
console.log(data);
const onSubmit = (e)=>{
e.preventDefault();
const mainnewsData = new FormData();
mainnewsData.append("ressort", data.ressort);
mainnewsData.append("theme", data.theme);
mainnewsData.append("title", data.title);
mainnewsData.append("content", data.content);
mainnewsData.append("img", fileData);
for(let value of mainnewsData){
console.log(value);
}
dispatch(updateMainNews(mainnewsData))
}
That is my form:
<MainNewsForm onSubmit={onSubmit}>
<Formgroup>
<Label htmlFor="img">Image</Label>
<Input type="file" name="img" id="img"style={{background:"var(--blue)", color:"var(--white)"}} accept=".png, .jpg" onChange={fileChange} ref={fileInput}/>
{preview ? <img src={preview} alt={preview} title={preview} style={{height:"200px", width:"400px"}}/> :
<img src={mainnews.img} alt="savedImg" title="savedImg" style={{height:"200px", width:"300px"}}/>}
</Formgroup>
<Formgroup>
<Label htmlFor="ressort">Ressort</Label>
<Input type="text" name="ressort" id="ressort" defaultValue={ressort} onChange={(e)=>setData({...data, ressort: e.target.value})}/>
</Formgroup>
<Formgroup>
<Label htmlFor="theme">Theme</Label>
<Input type="text" name="theme" id="theme" defaultValue={theme} onChange={(e)=>setData({...data, theme: e.target.value})}/>
</Formgroup>
<Formgroup>
<Label htmlFor="title">Title</Label>
<Input type="text" name="title" id="title" defaultValue={title} onChange={(e)=>setData({...data, title: e.target.value})}/>
</Formgroup>
<Formgroup>
<Label htmlFor="content">Content</Label>
<Textarea type="text" name="content" id="content" defaultValue={content} onChange={(e)=>({...data, content: e.target.value})}></Textarea>
</Formgroup>
<ButtonHolder>
<UpdateButton type="submit">Update</UpdateButton>
</ButtonHolder>
</MainNewsForm>
onChange={updateData} does not work since your change handler is written as a higher order function that returns a change handler that is custom for the specific inputs. onChange={updateData("ressort")} etc. should work however.
Couple of unrelated things I'd like to mention:
Using capitalized html tags like <Input looks a bit confusing, like you're using Material UI. The convention is that those are your own classes/component names or come from a library. I'd stick to lowercase html tags.
I would not put FormData in redux. If there is an async action that calls the backend, then somewhere in the action creator (or some sort of API client) is a better place to transform the data, if the API expects classic FormData. React doesn't need to be concerned with that.

How to bind an input's value to a link in react

I want to create a simple application where you can search for images. The application is written in React using fetch (I can't use axios) and Unsplash API. My current attempt renders a list of images with a static value "cars" into the link as shown: https://api.unsplash.com/search/photos?query=**cars**
In the code example below I am using a variable "${query}" to be able to search for images but it toes not work. I need help to figure out how to fix that. Thanks in advance!
code:
import React from "react";
import { useState, useEffect } from "react";
export default function App() {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [query, setQuery] = useState("");
useEffect(() => {
fetch(`https://api.unsplash.com/search/photos?query=${query}`, {
headers: {
Authorization: "Client-ID UnsplashId",
},
})
.then((response) => {
if (!response.ok) {
throw new Error(
`This is an HTTP error: The status is ${response.status}`
);
}
return response.json();
})
.then((actualData) => {
console.log(actualData);
setData(actualData.results);
setError(null);
})
.catch((err) => {
setError(err.message);
setData(null);
});
}, []);
return (
<div>
{/* onSubmit={this.handleSubmit} */}
<form>
<label>
<input
placeholder="Search"
type="text"
// value={this.state.value}
// value="cars"
onChange={(e) => setQuery(e.target.value)}
/>
</label>
<input type="submit" value="Submit" />
</form>
{data &&
data.map(({ id, description, urls }) => (
<img key={id} alt={description} src={urls.regular} />
))}
</div>
);
}
I think you want to achieve conditionally firing an effect
Example
useEffect(() => {
// This will execute whenever 'query' variable changes.
}, [ query ]);
// You can bind the state using the 'value' attribute.
<input
placeholder="Search"
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
I did not quietly get the question but I think you want to do the search every time the input is changed, hence I recommend using an input instead of the form and adding "query" to the "useEffect" conditions:
useEffect(() => {
fetch(`https://api.unsplash.com/search/photos?query=${query}`, {
headers: {
Authorization: "Client-ID UnsplashId",
},
})
.then((response) => {
// here
}, [query]);
<input
placeholder="Search"
type="text"
onChange={(e) => setQuery(e.target.value)} />

Resources