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

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)} />

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.

React UI/dom is not updating after data insertion in json-server

I am learning React from few days and I am trying to learn Axios, Everything worked fine until I tried to insert data, which I successfully inserted but My React Page did not updated contact list immediately.
HERE's MY CODE:
App.js
import Axios from "axios";
import React, { useEffect, useState } from "react";
import Add__Contact from "./api/Add__Contact";
const App = () => {
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [contacts, setContacts] = useState([]);
const url = "http://localhost:3006/contacts";
//get all availbale contacts
useEffect(() => {
// get all contacts async
async function getUsers() {
Axios.get(url).then((response) => {
setContacts(response.data);
});
}
getUsers();
console.log(contacts);
// get all contacts non-async
// Axios.get(url).then((response) => {
// setContacts(response.data);
// });
}, []);
//add new contact to server
const addContact = () => {
const saveRes = Add__Contact({ name, phone });
};
// view
return (
<div>
<h4>Add contact</h4>
<div>
<input type="text" name="name" value={name} onChange={(e) => setName(e.target.value)} placeholder="name here" />
<br />
<br />
<input
type="text"
name="phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
placeholder="Phone here"
/>
<br />
<br />
<button onClick={addContact}>Add to Contact</button>
</div>
<hr />
<h4>List of Contacts</h4>
<div>
{contacts.map((contact) => {
return (
<div key={contact.id}>
<span>{contact.name} : </span>
<span> {contact.phone}</span>
</div>
);
})}
</div>
</div>
);
};
export default App;
Add__Contact.js
import Axios from "axios";
const Add__Contact = async ({ name, phone }) => {
Axios({
method: "post",
url: "http://localhost:3006/contacts",
headers: {
"Content-Type": "application/json",
},
data: {
name,
phone,
},
}).then(function (res) {
// console.log(res);
});
};
export default Add__Contact;
db.json
{
"contacts": [
{
"name": "Max",
"phone": "123456",
"id": 1
},
{
"name": "John",
"phone": "13454",
"id": 2
},
{
"name": "Candy",
"phone": "1245781245",
"id": 3
}
]
}
I am not sure why it's not updating list automatically, I thought useEffect will run everytime I click and call Add__Contact(). Can you please tell me what did i missed or doing wrong?
I am not sure if useEffect hook is good for what I want to achieve or not, so please guide me. Thank you in advance.
data insertion is working fine, but after I insert it, it's not updating ui, even if I am fetching data inside useEffect
Your useEffect hook is only ran once - when the component mounts. This is because you have given it an empty dependency array (the 2nd argument).
The dependency array determines when the effect function will run. If its empty, it will only run when the component is mounted (displayed for the very first time). If you add something in the array, the effect will run on mount, and whenever the provided value changes.
In your case, you have an event (the click event from the Add to Contacts button) after which you want your data to be fetched again. But you also want to fetch data when the page loads.
One way to do it is something like this:
const Add__Contact = async ({ name, phone }) => {
// Return the Promise returned from the Axios call
return Axios({
method: "post",
url: "http://localhost:3006/contacts",
headers: {
"Content-Type": "application/json",
},
data: {
name,
phone,
},
});
};
const App = () => {
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [contacts, setContacts] = useState([]);
const url = "http://localhost:3006/contacts";
// Add a function to fetch contacts
const fetchContacts = async () => {
const res = await Axios.get(url);
setContacts(res.data);
};
// Effect that fetches contacts when the component loads
useEffect(() => {
fetchContacts();
}, []);
//add new contact to server
const addContact = async () => {
// await the Promise returned
const saveRes = await Add__Contact({ name, phone });
// Fetch the contacts list again
await fetchContacts();
};
// view
return (
<div>
<h4>Add contact</h4>
<div>
<input type="text" name="name" value={name} onChange={(e) => setName(e.target.value)} placeholder="name here" />
<br />
<br />
<input
type="text"
name="phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
placeholder="Phone here"
/>
<br />
<br />
<button onClick={addContact}>Add to Contact</button>
</div>
<hr />
<h4>List of Contacts</h4>
<div>
{contacts.map((contact) => {
return (
<div key={contact.id}>
<span>{contact.name} : </span>
<span> {contact.phone}</span>
</div>
);
})}
</div>
</div>
);
};
So you'r Contacts Array is not updated .Even you got a data from axios call .
Like if axios is returning data then i think you'r state in not updating then you have to use
setContacts((prv)=>[...prv,...res.data])
If you'r facing problem on Add time . Then make a separate function then use that in useEffect() && your ADD_Contact() .
const App = () => {
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [contacts, setContacts] = useState([]);
const getContacts = async () => {
const res = await Axios.get('http://localhost:3006/contacts');
setContacts(res.data);
};
useEffect(() => {
getContacts();
}, []);
const addContact = async () => {
const saveRes = await Add__Contact({ name, phone });
await getContacts();
};
return (
<div>
<h4>Add contact</h4>
<div>
<input type="text" name="name" value={name} onChange={(e) => setName(e.target.value)} placeholder="name here" />
<br />
<br />
<input
type="text"
name="phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
placeholder="Phone here"
/>
<br />
<br />
<button onClick={addContact}>Add to Contact</button>
</div>
<hr />
<h4>List of Contacts</h4>
<div>
{contacts.map((contact) => {
return (
<div key={contact.id}>
<span>{contact.name} : </span>
<span> {contact.phone}</span>
</div>
);
})}
</div>
</div>
);
};

How do I store an image in react useState

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))
}}

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" />

Problem updating data in an api with mui TextField inputs

I'm making a post admin tool with jsonplaceholder api and I'm having a problem with the put method. How can I update the text input modifications? It's not working even with the fixed data.
const EditPost = ({ match }) => {
const [state, setState] = useState({
title: "",
body: "",
});
const [error] = useState("");
const { id } = match.params;
const [post, setPost] = useState(null);
useEffect(() => {
axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then((response) => {
setPost(response.data);
});
}, []);
const updatePost = () => {
axios
.put(`https://jsonplaceholder.typicode.com/posts/${id}`, {
title: "Hello World!",
body: "Post updated."
})
.then((response) => {
setPost(response.data);
});
}
<form noValidate autoComplete="off" onSubmit={updatePost}>
<TextField
id="outlined-basic"
label="Title"
defaultValue={post.title || ""}
name="title"
type="text"
onChange="" />
<TextField
id="outlined-multiline-flexible"
label="Body"
defaultValue={post.body || ""}
name="body"
type="text"
onChange="" />
<Button
type="submit">
Update Post
</Button>
I would suggest you restructure your component as such:
const EditPost = ({ match }) => {
const [error] = useState("");
const { id } = match.params;
const [postTitle, setPostTitle] = useState("");
const [postBody, setPostBody] = useState("");
useEffect(() => {
axios
.get(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then((response) => {
setPost(response.data);
});
}, []);
const updatePost = () => {
axios
.put(`https://jsonplaceholder.typicode.com/posts/${id}`, {
title: postTitle,
body: postBody,
})
.then(({ data }) => {
setPostTitle(data.title);
setPostBody(data.body);
});
};
return (
<form noValidate autoComplete="off" onSubmit={updatePost}>
<TextField
id="outlined-basic"
label="Title"
value={postTitle}
name="title"
type="text"
onChange={(e) => setPostTitle(e.target.value)}
/>
<TextField
id="outlined-multiline-flexible"
label="Body"
value={postBody}
name="body"
type="text"
onChange={(e) => setPostBody(e.target.value)}
/>
<Button type="submit">Update Post</Button>
</form>
);
};
To be able to update the state as the user types, you need to control your form fields. The way to do this is to supply each field with a value prop, and use the onChange prop to update the state of the fields. This is the recommended by the mui team.
Furthermore, we have moved our state from a single object (which is reminiscent of the days of class components), to separate state objects (postBody and postTitle) which should reduce the number of times parts of the component have to re-render.

Resources