Mapping data with hooks in React after new data submit - javascript

My problem is I print the list of movies with map and I want the list to automatically print after submit without refreshing the page:
const [movieName, setMovieName] = useState('');
const [movieReview, setMovieReview] = useState('');
const [movieReviewsList, setMovieReviewsList] = useState([]);
useEffect(() => {
Axios.get('http://localhost:3001/api/get')
.then((response) => {
setMovieReviewsList(response.data);
})
}, []);
const submitReview = () => {
Axios.post('http://localhost:3001/api/insert', {
movieName: movieName,
movieReview: movieReview,
});
setMovieReviewsList([...movieReviewsList, {
movieName: movieName,
movieReview: movieReview
}]);
}
After database submit, I add a newly added movie to the list and try to map it so that a new movie will appear without refreshing the page:
{movieReviewsList.map((movie) => {
return (
<div key={movie.idmovie_reviews}>
<h3>Movie name: {movie.movie_name} </h3>
<h3>Movie review: </h3>
<p>{movie.movie_review}</p>
</div>
);
})}
I don't add a key when inserting the value to the database, where in my SQL db it is auto-incremented. Is there any way to add it so that I will be able to map it without refreshing?

Your issue description is so short to solve.
I guess your form is submitted into server as sync.
const onSubmit = (e: React.FormEvent) => {
e.preventDefault()
...
}
<form onSubmit={onSubmit}>
// ...
<button type='submit'>submit</button>
</form>

Related

Handling data rendering on redux state change

I'm trying to setup a form. It has Edit feature where on edit I call an API and get the data into state.
I'm struggling to display data in the form after api call. There's no problem utilizing the API or calling the redux functions. Problem is that my Form only displays last data in the redux state but not the updated data.
That's how I'm doing the stuff.
Calling API if isEdit===True at the same time Form is being displayed on component mount.
Updateding state after success as an object called customer
accessing the customer object like this
const { customer } = useSelector((state) => state.customers)
Lets say I have a input field where I want to display the email of customer.
I'm handling this think like that:
email: isEdit ? customer?.email : '', // At this point there is some problem
It loads the previous data that was stored in the state.customer but not the new one.
I believe my email field is rendering first and then doesn't updated the value when change happens in state.customer.
So how I can fix this? So that email value should be changed at the same time if state.customer got changed
Here is the full component. Still removed irrelevant part.
const CustomerNewEditForm = ({ isEdit, id, currentUser}) => {
const dispatch = useDispatch()
const navigate = useNavigate()
console.log('isEdit', isEdit, 'id', id, 'currentUser', currentUser)
// get sales reps
const { customer } = useSelector((state) => state.customers)
// const customer = () => {
// return isEdit ? useSelector((state) => state.customers?.customer) : null
// }
const { enqueueSnackbar } = useSnackbar()
const defaultValues = useMemo(
() => ({
email: isEdit ? customer?.email : '',
name: isEdit ? customer?.name : '',
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[currentUser]
)
const methods = useForm({
resolver: yupResolver(NewUserSchema),
defaultValues
})
const {
reset,
watch,
control,
setValue,
handleSubmit,
formState: { isSubmitting }
} = methods
const values = watch()
useEffect(() => {
if (isEdit === true) {
dispatch(getCustomerDetails(id))
console.log(customer)
}
if (isEdit && currentUser) {
reset(defaultValues)
}
if (!isEdit) {
reset(defaultValues)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isEdit, currentUser])
const onSubmit = async () => {
try {
await new Promise((resolve) => setTimeout(resolve, 500))
reset()
let body = {
email: values.email,
name: values.name,
}
console.log(body)
dispatch(createCustomer(body))
enqueueSnackbar(!isEdit ? 'Create success!' : 'Update success!')
// navigate(PATH_DASHBOARD.admin.root)
} catch (error) {
console.error(error)
}
}
return (
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<Grid item md={3}>
{' '}
<RHFTextField name="name" label="Customer Name" />
</Grid>
<Grid item md={3}>
{' '}
<RHFTextField name="email" label="Email Address" />
</Grid>
</FormProvider>
)
}
export default CustomerNewEditForm
Here in the component defaultValues carries the previous data from customer object if its True and renders the form with those values. but new data comes a miliseconds later but form renders first.
First of all try to console.log your customer data and make sure that it gets a fresh data on last render.
If it gets fresh data, try take a look at your Input component, it might set some initial data, so the input will be editable and controlled by some state.
Try to modify your input's state on redux store update in useEffect.
Currently that's all that I can suggest, update your post with code with your form and input, also post your console.log result, if my answer doesn't helped you.
If the problem would be not in form\input state and console.log wouldn't show you actual updated data in last render, then I will need to see your redux store code to resolve this issue.
Hope it helped

How to save data after reloading the page

I have working code that helps me create a to-do list. Everything works almost well. When I click on the "Enter" key, my page reloads. Similarly, after reloading the page, all created elements disappear. I have 2 questions: can you show how to save all created elements after reloading and how to avoid reloading by pressing on "Enter"? Thank you very much
import React, {useState} from "react";
export function Creating_List () {
let [allTasks, setAllTasks] = useState([]);
let [input, setInput] = useState('');
let addTask = (myInput) => {
if (myInput){
let newTask = {
id: Math.random().toString(36).substr(2,9),
task: myInput,
complete: false
}
setAllTasks([...allTasks, newTask])
}
}
let taskDone = (id) => {
setAllTasks([allTasks.filter((todo => todo.id !== id))])
}
let handleInput = (e) => {
setInput(e.currentTarget.value)
}
let submitTask = (e) => {
e.preventDefault();
addTask(input);
setInput('');
}
return (<div className='tasks'>
<h1>Список задач {allTasks.length}</h1>
<form>
<input
type="text"
value={input}
onChange={handleInput}
placeholder="Нове завдання"
/>
</form>
<button onClick={submitTask} type="submit">Створити</button>
<div>
{allTasks.map(el => <div key={el.id}>{el.task} <button onClick={taskDone}>Виконано</button> </div>)}
</div>
</div>)
}
Change your <form> to <form onSubmit = {submitTask}>, this will prevent the page from reloading on enter since you have e.preventDefault(). If you want data to persist after reloading, you can use localStorage or a database like Firebase or MongoDB, or you can create your own backend using Node.js.

how to stop fetching data of shorten URL API because it's continuously adding same fetched link data with multiple different shortened versions

Using https://shrtco.de/docs/ for fetching my input data but it's continuously fetching it and adding to my displayLink array with different short url versions. I can add another link and it gets added to it but how do i stop it from duplicating or filter out a single originial link?
I get output like this screenshot of logged output & it keeps on adding to that displayLinks state with more same link but diff versions.
import { useForm } from "react-hook-form";
import axios from 'axios';
import Loading from '../../images/Ripple-1s-200px.svg'
const Shorten = () => {
// get built in props of react hook form i.e. register,handleSubmit & errors / watch is for devs
const { register, handleSubmit, formState: {errors} } = useForm();
//1. set user original values to pass as params to url
const [link, setLink] = useState('');
//2. set loader initial values to false
const [loading, setLoading] = useState(false);
//3. pass the fetched short link object into an array so we can map
const [displayLinks, setDisplayLinks] = useState([]);
//fetch the shortened url link using async method to show loading
useEffect(() => {
let unmounted = false;
async function makeGetRequest() {
try {
let res = await axios.get('https://api.shrtco.de/v2/shorten', { params: { url: link } });
//hid loader if u get response from api call
if (!unmounted && res.data.result.original_link !== displayLinks.original_link) {
setLoading(false);
//add the data to allLinks array to map
return setDisplayLinks(displayLinks => [...displayLinks, res.data.result]);
}
}
catch (error) {
console.log(error, "inital mount request with no data");
}
}
//invoke the makeGetRequest here
makeGetRequest();
return () => {
unmounted = true;
}
//passing dependency to re render on change of state value
}, [displayLinks, link]);
// onSubmit form log data into a variable
const onSubmit = (data, event) => {
event.preventDefault();
//puttin data in a variable to pass as url parameter if valid
setLink(data.userLink);
//add loading here after data is set to state
setLoading(!false);
}
return (
<div>
<form onSubmit={handleSubmit(onSubmit)}>
<label></label>
<input
{...register("userLink", {required: "Please add a link"})}
type="url"
id="userLink"
/>
{errors.userLink && <span>{errors.userLink.message}</span>}
<input type="submit" />
</form>
{
loading ?
<div className="loader" id="loader">
<img src={Loading} alt="Loading" />
</div>
: <div>
{
displayLinks.map((el) => {
return (
<div key={el.code}>
<div>
<h5>{el.original_link}</h5>
</div>
<div>
<h5>{el.full_short_link}</h5>
<button>Copy</button>
</div>
</div>
)
})
}
</div>
}
</div>
)
}
export default Shorten;

React async state management

I hate to upload a code snippet with no sandbox, but this particular instance I use firebase so wasn't sure how to make one. Apologies for the verbose code. I'm a beginner React developer and I've been stuck on this state management issue for 2 weeks now, and I tried so many different methods but to no fruit. Please help.
My goal is to click AddLinkButton to make multiple input forms one by one, each input form would be different links, and by clicking Apply Button it would collect all the link values and store it to firebase's firestore. Once the storing is complete, it would display a preview by passing in multiple updated hook values to <UserPreview />.
If I run this particular code below, the key which is supposed to be the value of the link input forms, is null and does not update on onChange.
Please help... much appreciated. Thank you.
EDIT: changed variable name key to keyHook but to no success. Same issue
const AdminCustomizer = () => {
const [username, setUsername] = useState(null);
const [linkForm, setlinkForm] = useState([]);
const [spotlightLabel, setSpotlightLabel] = useState('');
const [spotlightLink, setSpotlightLink] = useState('');
const [refresh, setRefresh] = useState(false);
const [keyHook, setKeyHook] = useState(null);
const [startCollect, setStartCollect] = useState(false);
const linkRef = useRef();
const userInfo = {username, linkRef, spotlightLabel, spotlightLink, pfpURL, refresh};
// on initial load, load database to page
if (!username) {
firebase.getAuth().onAuthStateChanged(user => {
if (user) {
setUsername(user.displayName);
firebase.getUserInfo(user.displayName).then(result => {
setSpotlightLabel(result.spotlightLabel);
setSpotlightLink(result.spotlightLink);
linkRef.current = result.links;
if (result.links) {
Object.values(result.links).forEach(link => {
AddLinks(link);
});
}
})
}
});
}
//on refresh (when clicking apply changes button) reload page values with updated database
useEffect(() => {
if (refresh) {
firebase.getAuth().onAuthStateChanged(user => {
if (user) {
firebase.getUserInfo(user.displayName).then(result => {
linkRef.current = result.links;
Object.values(result.links).forEach(link => {
AddLinks(link);
});
})
setRefresh(false);
}
});
}
}, [refresh])
// adding AddLink button will add a new input form
// adding AddLink with firebase database value will add a new input form with values loaded
const AddLinks = url => {
const hooks = { refresh, startCollect, keyHook, setKeyHook };
if (url) setKeyHook(url);
setlinkForm([ ...linkForm, <AddLink key={keyHook} keyHook={keyHook} hooks={hooks} /> ]);
}
// add link input form
const AddLink = props => {
const handleChange = e => setKeyHook(e.target.value);
return (
<form noValidate autoComplete="off">
<br />
<Link label="Social" onChange={handleChange} value={props.keyHook} />
</form>
)
}
// when apply changes is clicked, collect input values from all link input forms
if (startCollect) {
linkForm.forEach(form => {
linkRef.current = {
...linkRef.current,
link: form.keyHook,
}
});
firebase.addLinksToUser({ spotlightLabel, spotlightLink, linkRef }).then(() => {
//force refresh to update userInfo for UserPreview
setStartCollect(false);
setRefresh(true);
});
}
return (
<>
<LinkBox>
<ApplyButton onClick={() => setStartCollect(true)}>Apply Changes</ApplyButton>
<Link label="Website Title" onChange={e => setSpotlightLabel(e.target.value)} value={spotlightLabel} />
<Link label="Website URL" onChange={e => setSpotlightLink(e.target.value)} value={spotlightLink}/>
<AddLinkButton onClick={() => AddLinks(null)} />
<div>{linkForm ? linkForm.map(child => child) : null}</div>
</LinkBox>
<div>
<PhoneOutline>
<UserPreview userInfo={userInfo}/>
</PhoneOutline>
</div>
</>
);
}
export default AdminCustomizer;
In AddLink, the key is a restricted keyword and doesn't get propagated as props. Try a different prop name instead of key.
See this link
Try:
<AddLink key={keyHook} keyHook={keyHook} hooks={hooks} />

How to fix the problem of passing the value of the select to the server?

I have React app. In this app I have page with list of image categories which I get from local server and second page with posts. I also created a form that adds new post in local server. In this form I have four input : ( title, category_id, description, image).
In input category_id I write number id of category. But now I need that there was not input but there was select with titles of category, which I get from server using API method GET. And I tried to make such a select.
But my server response error:
Category_id is required
It's happening because:
I pass to the server title category, but I should pass Id category. Title should be just show us name post in select but I pass id to server.
Component SelectCategory not related with FormData. Therefore value which is selected SelectCategory will not appear in FormData and then will not appear in body method handleSubmit.
How to fix this problem?
response from serever(list of categories which I get in const data):
{"data":
[{"id":20,"title":"auto"},
{"id":21,"title":"sport"},
{"id":23,"title":"new"}
]}
AddPost.js:
const AddPost = () => {
const formRef = useRef();
const [category, setCategory] = useState(''); // <-- category which I select in select will fall here
const [categories, setCategories] = useState([]); // <-- a list of existing categories will fall here
useEffect(() => {
fetchData();
}, []);
async function fetchData() { // <-- get List of categories
const data = await api(`${listRoute}`, { method: 'GET'});
setCategories(data.data.map(item => item.title)); // <-- now I set title of category in select
}
const handleSubmit = async (event) => { // <-- send form to local server
const data = new FormData(formRef.current); // <-- value from my from will fall here
event.preventDefault();
const response = await apiImage(`${imageRoute}`, {
method: 'POST',
body: data,
});};
const upadateSelectCategory = e => { // <-- choose category in select in form
setCategory(e.target.value);
};
return (
<div>
<form onSubmit={handleSubmit} ref={formRef}>
<input type="text" name="title"/>
<input type="text" name="description"/>
<input type="file" name="image" accept="image/*"/>
/*<input type="text" name="category_id"/>*/ // <-- Instead this input now I write SelectCategory:
<SelectCategory upadateSelectCategory={upadateSelectCategory} categories={categories} value={category} />
<button type="submit">Add</button>
</form>
</div>
);
};
SelectCategory.js:
export default (props) => {
return (
<div>
<select onChange={props.upadateSelectCategory} value={props.value}>
<option value="">-- Category --</option>
{props.categories.map(item => <option key={item}>{item}</option>)}
</select>
</div>
);}
You have to send category_id and what now you are store is only title.
You have to store id, too.
async function fetchData() {
const data = await api(`${listRoute}`, { method: 'GET'});
setCategories(data.data); // Change here
}
and In your SelectCategory component:
export default (props) => {
return (
<div>
<select onChange={props.upadateSelectCategory} value={props.value}>
<option value="">-- Category --</option>
{props.categories.map(item => <option value={item.id} key={item.id}>{item.title}</option>)}
</select>
</div>
);}
here set category_id as value of option.
and in handleSubmit add this code after const data = new FormData(formRef.current);:
data.append('category_id', category)

Categories