import "./App.css";
import { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { addUser} from "./features/Users";
function App() {
const dispatch = useDispatch();
const userList = useSelector((state) => state.users.value);
const [name, setName] = useState("");
const [username, setUsername] = useState("");
return (
<div className="App">
{" "}
<div className="addUser">
<input
type="text"
placeholder="Name..."
onChange={(event) => {
setName(event.target.value);
}}
/>
<input
type="text"
placeholder="Username..."
onChange={(event) => {
setUsername(event.target.value);
}}
/>
<button
onClick={() => {
dispatch(
addUser({
id: userList[userList.length - 1].id + 1,
name,
username,
})
);
}}
>
{" "}
Add User
</button>
</div>
);}
I am new to react and redux. After clicking the "Add User" button, new User data from inputs in the code will be added to the backend list. I want the values in input sections to be cleared after clicking the "Add User" button, but I don't know how to do.
you need to clear your state after click on submit button. for ex: set function like =>
const clearData = {
setName("")
setUsername("")
}
and pass the func to your onClick event.
onClick={clearData}
The following code will work perfectly fine.
Just assign value={name} and value={username} to both input types respectively and when you click Add User just clear the data in both the states.
import "./App.css";
import { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { addUser} from "./features/Users";
function App() {
const dispatch = useDispatch();
const userList = useSelector((state) => state.users.value);
const [name, setName] = useState("");
const [username, setUsername] = useState("");
return (
<div className="App">
{" "}
<div className="addUser">
<input
type="text"
placeholder="Name..."
value={name}
onChange={(event) => {
setName(event.target.value);
}}
/>
<input
type="text"
placeholder="Username..."
value={username}
onChange={(event) => {
setUsername(event.target.value);
}}
/>
<button
onClick={() => {
setName("");
setUsername("");
dispatch(
addUser({
id: userList[userList.length - 1].id + 1,
name,
username,
})
);
}}
>
{" "}
Add User
</button>
</div>
);}
You can maintain a simple variable with list of form fields and can update the form state with the variable when you needed to clear form data. The below approach comes handy when you need to add additional fields as well.
import "./App.css";
import { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { addUser} from "./features/Users";
const formFields = { name: '', username: '' };
function App() {
const dispatch = useDispatch();
const userList = useSelector((state) => state.users.value);
const [params, setParams] = useState(formFields)
const handleChange = (e) => {
const { name, value } = e.target;
setParams({ ...params }, ...{[name]: value});
}
const clearForm = () => setParams(formFields);
return (
<div className="App">
<div className="addUser">
<input
type="text"
placeholder="Name..."
value={params.name}
onChange={(e) => handleChange(e)}
/>
<input
type="text"
placeholder="Username..."
value={params.username}
onChange={(e) => handleChange(e)}
/>
<button
onClick={() => {
dispatch(
addUser({
id: userList[userList.length - 1].id + 1,
...params
})
);
clearForm();
}}
>
{" "}
Add User
</button>
</div>
</div>
)
}
Related
I keep on getting this error when trying to update a product in my project:
react_devtools_backend.js:4012 Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.
Index.js:
import React from 'react';
import { Provider } from 'react-redux';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import store from './store';
ReactDOM.render(
<Provider store={store}>
<React.StrictMode>
<App />
</React.StrictMode>
</Provider>,
document.getElementById('root')
);
serviceWorker.unregister();
ProductList:
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link, useParams, useNavigate, useLocation } from 'react-router-dom';
import {
createProduct,
deleteProduct,
listProducts,
} from '../actions/productActions';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';
import {
PRODUCT_CREATE_RESET,
PRODUCT_DELETE_RESET,
} from '../constants/productConstants';
export default function ProductListScreen(props) {
const navigate = useNavigate();
const { pageNumber = 1 } = useParams();
const { pathname } = useLocation();
const sellerMode = pathname.indexOf('/seller') >= 0;
const productList = useSelector((state) => state.productList);
const { loading, error, products, page, pages } = productList;
const productCreate = useSelector((state) => state.productCreate);
const {
loading: loadingCreate,
error: errorCreate,
success: successCreate,
product: createdProduct,
} = productCreate;
const productDelete = useSelector((state) => state.productDelete);
const {
loading: loadingDelete,
error: errorDelete,
success: successDelete,
} = productDelete;
const userSignin = useSelector((state) => state.userSignin);
const { userInfo } = userSignin;
const dispatch = useDispatch();
useEffect(() => {
if (successCreate) {
dispatch({ type: PRODUCT_CREATE_RESET });
navigate(`/product/${createdProduct._id}/edit`);
}
if (successDelete) {
dispatch({ type: PRODUCT_DELETE_RESET });
}
dispatch(
listProducts({ seller: sellerMode ? userInfo._id : '', pageNumber })
);
}, [
createdProduct,
dispatch,
navigate,
sellerMode,
successCreate,
successDelete,
userInfo._id,
pageNumber,
]);
const deleteHandler = (product) => {
if (window.confirm('Are you sure to delete?')) {
dispatch(deleteProduct(product._id));
}
};
const createHandler = () => {
dispatch(createProduct());
};
return (
<div>
<div className="row">
<h1>Products</h1>
<button type="button" className="primary" onClick={createHandler}>
Create Product
</button>
</div>
{loadingDelete && <LoadingBox></LoadingBox>}
{errorDelete && <MessageBox variant="danger">{errorDelete}</MessageBox>}
{loadingCreate && <LoadingBox></LoadingBox>}
{errorCreate && <MessageBox variant="danger">{errorCreate}</MessageBox>}
{loading ? (
<LoadingBox></LoadingBox>
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
<>
<table className="table">
<thead>
<tr>
<th>ID</th>
<th>NAME</th>
<th>PRICE</th>
<th>CATEGORY</th>
<th>BRAND</th>
<th>ACTIONS</th>
</tr>
</thead>
<tbody>
{products.map((product) => (
<tr key={product._id}>
<td>{product._id}</td>
<td>{product.name}</td>
<td>{product.price}</td>
<td>{product.category}</td>
<td>{product.brand}</td>
<td>
<button
type="button"
className="small"
onClick={() => navigate(`/product/${product._id}/edit`)}
>
Edit
</button>
<button
type="button"
className="small"
onClick={() => deleteHandler(product)}
>
Delete
</button>
</td>
</tr>
))}
</tbody>
</table>
<div className="row center pagination">
{[...Array(pages).keys()].map((x) => (
<Link
className={x + 1 === page ? 'active' : ''}
key={x + 1}
to={`/productlist/pageNumber/${x + 1}`}
>
{x + 1}
</Link>
))}
</div>
</>
)}
</div>
);
}
Product Edit Page:
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import Axios from 'axios';
import { useNavigate, useParams } from 'react-router-dom';
import { detailsProduct, updateProduct } from '../actions/productActions';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';
import { PRODUCT_UPDATE_RESET } from '../constants/productConstants';
export default function ProductEditScreen(props) {
const navigate = useNavigate();
const params = useParams();
const { id: productId } = params;
const [name, setName] = useState('');
const [price, setPrice] = useState('');
const [image, setImage] = useState('');
const [category, setCategory] = useState('');
const [countInStock, setCountInStock] = useState('');
const [brand, setBrand] = useState('');
const [description, setDescription] = useState('');
const productDetails = useSelector((state) => state.productDetails);
const { loading, error, product } = productDetails;
const productUpdate = useSelector((state) => state.productUpdate);
const {
loading: loadingUpdate,
error: errorUpdate,
success: successUpdate,
} = productUpdate;
const dispatch = useDispatch();
useEffect(() => {
if (successUpdate) {
navigate('/productlist');
}
if (!product || product._id !== productId || successUpdate) {
dispatch({ type: PRODUCT_UPDATE_RESET });
dispatch(detailsProduct(productId));
} else {
setName(product.name);
setPrice(product.price);
setImage(product.image);
setCategory(product.category);
setCountInStock(product.countInStock);
setBrand(product.brand);
setDescription(product.description);
}
}, [product, dispatch, productId, successUpdate, navigate]);
const submitHandler = (e) => {
e.preventDefault();
// TODO: dispatch update product
dispatch(
updateProduct({
_id: productId,
name,
price,
image,
category,
brand,
countInStock,
description,
})
);
};
const [loadingUpload, setLoadingUpload] = useState(false);
const [errorUpload, setErrorUpload] = useState('');
const userSignin = useSelector((state) => state.userSignin);
const { userInfo } = userSignin;
const uploadFileHandler = async (e) => {
const file = e.target.files[0];
const bodyFormData = new FormData();
bodyFormData.append('image', file);
setLoadingUpload(true);
try {
const { data } = await Axios.post('/api/uploads', bodyFormData, {
headers: {
'Content-Type': 'multipart/form-data',
Authorization: `Bearer ${userInfo.token}`,
},
});
setImage(data);
setLoadingUpload(false);
} catch (error) {
setErrorUpload(error.message);
setLoadingUpload(false);
}
};
return (
<div>
<form className="form" onSubmit={submitHandler}>
<div>
<h1>Edit Product {productId}</h1>
</div>
{loadingUpdate && <LoadingBox></LoadingBox>}
{errorUpdate && <MessageBox variant="danger">{errorUpdate}</MessageBox>}
{loading ? (
<LoadingBox></LoadingBox>
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
<>
<div>
<label htmlFor="name">Name</label>
<input
id="name"
type="text"
placeholder="Enter name"
value={name}
onChange={(e) => setName(e.target.value)}
></input>
</div>
<div>
<label htmlFor="price">Price</label>
<input
id="price"
type="text"
placeholder="Enter price"
value={price}
onChange={(e) => setPrice(e.target.value)}
></input>
</div>
<div>
<label htmlFor="image">Image</label>
<input
id="image"
type="text"
placeholder="Enter image"
value={image}
onChange={(e) => setImage(e.target.value)}
></input>
</div>
<div>
<label htmlFor="imageFile">Image File</label>
<input
type="file"
id="imageFile"
label="Choose Image"
onChange={uploadFileHandler}
></input>
{loadingUpload && <LoadingBox></LoadingBox>}
{errorUpload && (
<MessageBox variant="danger">{errorUpload}</MessageBox>
)}
</div>
<div>
<label htmlFor="category">Category</label>
<input
id="category"
type="text"
placeholder="Enter category"
value={category}
onChange={(e) => setCategory(e.target.value)}
></input>
</div>
<div>
<label htmlFor="brand">Brand</label>
<input
id="brand"
type="text"
placeholder="Enter brand"
value={brand}
onChange={(e) => setBrand(e.target.value)}
></input>
</div>
<div>
<label htmlFor="countInStock">Count In Stock</label>
<input
id="countInStock"
type="text"
placeholder="Enter countInStock"
value={countInStock}
onChange={(e) => setCountInStock(e.target.value)}
></input>
</div>
<div>
<label htmlFor="description">Description</label>
<textarea
id="description"
rows="3"
type="text"
placeholder="Enter description"
value={description}
onChange={(e) => setDescription(e.target.value)}
></textarea>
</div>
<div>
<label></label>
<button className="primary" type="submit">
Update
</button>
</div>
</>
)}
</form>
</div>
);
}
what could be the problem?
The code should redirect me to a new page in which i can create a new product and add it to my database
What the code looks like rendering the button to show the form
import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { deleteSong, getSongs, updateSong } from '../../store/song';
import ReactAudioPlayer from 'react-audio-player';
import { useHistory } from 'react-router';
import SongForm from '../AddSongForm';
import EditSongForm from '../EditSongForm';
const SongList = () => {
const [addShowForm, setAddShowForm] = useState(false);
const [editShowForm, setEditShowForm] = useState(false);
const history = useHistory()
const dispatch = useDispatch();
const songsObj = useSelector((state) => state.songState.entries);
const songs = Object.values(songsObj)
const user = useSelector((state) => state.session.user);
const CurrentUserId = user?.id
const remove = (e) => {
dispatch(deleteSong(e.target.id));
}
const addFormCheck = (e) => {
if (addShowForm) setAddShowForm(false)
if (!addShowForm) setAddShowForm(true)
}
const editFormCheck = (e) => {
if (editShowForm) setEditShowForm(false)
if (!editShowForm) setEditShowForm(true)
}
useEffect(() => {
dispatch(getSongs());
}, [dispatch]);
return (
<div>
<div>
<button onClick={addFormCheck}>add a song</button>
{addShowForm ?
<SongForm />
: null}
</div>
<h1>Song List</h1>
<ol>
{songs.map(({ id, songName, songLink, userId }) => (
<div className='songdetails' key={id}>
<p key={id}>songName={songName}</p>
<ReactAudioPlayer
src={songLink}
autoPlay
controls
key={songLink}
/>
{userId === CurrentUserId ?
<>
<div>
<button id={id} onClick={remove}>remove</button>
</div>
<div>
<button id={id} onClick={editFormCheck}>edit</button>
{editShowForm ?
<EditSongForm props={id} />
: null}
</div>
</>
: null}
</div>
))}
</ol>
</div>
);
};
export default SongList;
The actual form
import { useState } from "react";
import { useDispatch } from "react-redux";
import { updateSong } from "../../store/song";
import { useSelector } from "react-redux";
const EditSongForm = ({ props }) => {
console.log(props)
const dispatch = useDispatch();
const [songName, setSongName] = useState("");
const [songLink, setSongLink] = useState("");
const [errors, setErrors] = useState([]);
const reset = () => {
setSongName("");
setSongLink("");
// setAlbumName('');
// setArtistName('')
};
const user = useSelector((state) => state.session.user);
const userId = user?.id
const handleSubmit = async (e) => {
e.preventDefault();
const updatedSongDetails = {
id: props,
songName,
songLink,
userId
};
let updatedSong = await dispatch(updateSong(updatedSongDetails))
.catch(async (res) => {
const data = await res.json()
if (data && data.errors) setErrors(data.errors)
})
reset();
};
return (
<div className="inputBox">
<h1>Update A Song</h1>
<ul>
{errors.map((error, idx) => <li className='errors' key={idx}>{error}</li>)}
</ul>
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={(e) => setSongName(e.target.value)}
value={songName}
placeholder="Song Name"
name="Song Name"
/>
<input
type="text"
onChange={(e) => setSongLink(e.target.value)}
value={songLink}
placeholder="Song Link"
name="Audio File"
/>
<button type="submit">Submit</button>
</form>
</div>
);
};
export default EditSongForm;
Right now when I have a list of songs and click the button for the edit form to appear it applies to the entire list if I have more than one song uploaded. I'm not sure how to make it specific enough so only one form opens at a time.
The solution was to create a component for specific song details and then render that in the .map.
<h1>Song List</h1>
<ol>
{songs.map(({ id, songName, songLink, userId }) => (
<div>
<SpecificSong id={id} songName={songName} songLink={songLink} userId={userId} />
</div>
))}
</ol>
I have some posts in my database I'm trying to retrieve and edit posts. The posts had some categories which I set as a checkbox. Well, I've retrieved a single post by id successfully but the problem is I also retrieved the categories and I want to show them as checked not all of them only those ones which are set for that particular post. I have another problem I cannot check the box anymore and am not able to add another category to the category list. Help me!
Here is the Edit Post page
import React, { useEffect, useState } from 'react';
import { Alert, Button, Card, Container, Form } from 'react-bootstrap';
import ReactMarkdown from 'react-markdown';
import { useDispatch, useSelector } from 'react-redux';
import { toast, ToastContainer } from 'react-toastify';
import { listCategory } from '../actions/categoryActions';
import { listPostDetails, updatePost } from '../actions/postActions';
const EditPost = ({ history, match }) => {
const postId = match.params.id;
const [categories, setCategories] = useState([]);
const dispatch = useDispatch();
const categoryList = useSelector((state) => state.categoryList);
const { categories: cateList } = categoryList;
const postDetails = useSelector((state) => state.postDetails);
const { post } = postDetails;
useEffect(() => {
if (!userInfo) {
history.push('/login');
}
dispatch(listCategory());
if (!post || post._id !== postId) {
dispatch(listPostDetails(postId));
} else {
setCategories(post.categories);
}
}, [dispatch, history, userInfo, post, postId, categories]);
const submitHandler = (e) => {
e.preventDefault();
dispatch(updatePost(title, desc, img, categories));
history.push('/my_posts');
};
return (
<div className=" createPost mt-4 py-4">
<ToastContainer />
<Container>
<h2>EDIT POST</h2>
<Form onSubmit={submitHandler}>
<Form.Group controlId="category" className="mb-2">
<Form.Label>Select Categories</Form.Label>
<br />
{cateList?.map((cate) => (
<Form.Check
inline
key={cate._id}
type="checkbox"
label={cate.name}
onChange={(e) => {
if (e.target.checked) {
setCategories([...categories, cate.name]);
} else {
setCategories(
categories?.filter((cat) => cat !== cate.name)
);
}
}}
/>
))}
</Form.Group>
<Button
type="submit"
variant="success"
style={{ letterSpacing: '2px', fontWeight: 'bold' }}>
CREATE
</Button>
</Form>
</Container>
</div>
);
};
export default EditPost;
I'm trying to edit an input value in a child component and send to the parent
:
https://codesandbox.io/s/sleepy-rain-skoss?file=/src/Editlabel.js:0-389
Parent:
import "./styles.css";
import EditLabel from "./Editlabel";
import { useEffect, useState } from "react";
export default function App() {
const [newName, setNewName] = useState();
useEffect(() => {
console.log("newName", newName);
}, [newName]);
return (
<div className="App">
<EditLabel
value={"hello"}
click={(changedName) => {
setNewName(changedName);
}}
/>
</div>
);
}
Child:
import React, { useState } from "react";
const EditLabel = ({ value, click }) => {
const [name, setName] = useState(value);
return (
<>
<input type={"text"} placeholder={name}></input>
<button
onClick={(e) => {
setName(e.target.value);
click(name);
}}
>
Edit
</button>
</>
);
};
export default EditLabel;
However, the console logs "hello" and then it just logs empty strings.
How can I make it work?
try this on your child's input box
<input type={"text"} placeholder={name} onChange={(e) => setName(e.target.value)}>
Change EditLabel to use a ref to capture the input value:
const EditLabel = ({ value, click }) => {
const inputRef = useRef(null);
return (
<>
<input ref={inputRef} type={"text"} placeholder={value}></input>
<button
onClick={() => {
click(inputRef.current.value);
}}
>
Edit
</button>
</>
);
};
Update App to use the values it gets via the click callback:
export default function App() {
const [newName, setNewName] = useState("hello");
useEffect(() => {
console.log("newName", newName);
}, [newName]);
return (
<div className="App">
<EditLabel
value={newName}
click={(changedName) => {
setNewName(changedName);
}}
/>
</div>
);
}
I have a form on 'products/add' and I add products to the database, after I submit the request, I redirect to the page where all the products are displayed. However, this page does not display information about the last item I added. How to fix it? How do I render pages after redirects to display the most current data?
'localhost:3333/products/add'
import React, {useState} from 'react';
import api from './api';
import { Redirect } from 'react-router'
const HandleProduct = () => {
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const [redirect, setRedirect] = useState(false);
const updateName = (e) =>{
setName(e.target.value);
}
const updateDescription = (e) =>{
setDescription(e.target.value);
}
const addProduct = (e) =>{
e.preventDefault();
const product = {
name: name,
description: description
}
api.addProduct(product)
.then((req, res) =>{
console.log(res);
setRedirect(true);
})
}
if(redirect) {
return <Redirect to={'/products'} />
}
return (
<div>
<form onSubmit={addProduct}>
<input type="text" name="name" value={name} onChange={updateName}/>
<input type="text" name="description" value={description} onChange={updateDescription}/>
<button>Submit</button>
</form>
</div>
);
}
export default HandleProduct;
List of products(localhost:3333/products):
import React, {useContext} from 'react';
import {ProductsContext} from './ProductsContext';
const ProductsList = () => {
const [data] = useContext(ProductsContext);
return (
<div>
{console.log(data)}
{data.products.map((product, index)=>(
<div key={index}>
<p>{product.name}</p>
<p><i>{product.description}</i></p>
</div>
))}
</div>
);
}
export default ProductsList;