I am building a searchbar displaying suggestions while the user writes in the input. It is working ok, but I want that if the user remove what he has wrote, any suggestion be shown. But it is not happening, when the user remove what he wrote, he still can see the suggestion list, as you can see in the image attached.
This is the code:
`function Search() {
const [suggestions, setSuggestions] = useState([]);
const [name, setName] = useState("");
const dispatch = useDispatch();
const history = useHistory();
const products = useSelector(state => state.products)
function handleInputChange(e) {
//setea el name con lo que va escribiendo el usuario
e.preventDefault();
setName(e.target.value);
let filteredProducts = products.filter(
(p) => p.name.toLowerCase().includes(e.target.value.toLowerCase())
);
setSuggestions(filteredProducts);
}
function handleSearch(e) {
e.preventDefault();
let findProduct = products.find(
(p) => p.name.toLowerCase().includes(name.toLowerCase())
); //busca el nombre dentro de la array de data
`if (!name) {
alert("Please, enter some name");
}
if (findProduct) {
dispatch(search(name)); //si lo encuentra se dispara la accion ####
history.push(`/product/${findProduct.id}`); //despues redirige para ver el detalle
// console.log(findProduct);
} else if (!findProduct) {
alert("That Product doesnt exist");
}
setName("");//vacia el input
setSuggestions([]);
}
function handleSuggestionClick(id) {
history.push(`/product/${id}`);
setName("");//vacia el input
setSuggestions([]);
}`
return (
<div className="searchContainer">
<div className="search_inputSuggest">
<input
id="search"
className="searchBar"
type="text"
placeholder="Search by name"
onChange={(e) => handleInputChange(e)}
value={name}
/>
<div className="search_suggestion_div">
<datalist className="suggestionsList">
{suggestions.slice(0, 10).map(s => ( //shows just 10 suggestions
<option className="suggestionsList_item" key={s.id} onClick={() => handleSuggestionClick(s.id)}>
{s.name}
</option>
))}
</datalist>
</div>
</div>
<button className="btnSearch" onClick={(e) => handleSearch(e)}>
Buscar
</button>
</div>
);
}
export default Search;`
I also tried this:
{suggestions.length > 0 ? (
<div className="search_suggestion_div">
<datalist className="suggestionsList">
{suggestions.slice(0, 10).map(s => ( //shows just 10 suggestions
<option className="suggestionsList_item" key={s.id} onClick={() => handleSuggestionClick(s.id)}>
{s.name}
</option>
))}
</datalist>
</div>
) : (
null
)
}
And:
` {suggestions.length &&
<div className="search_suggestion_div">
<datalist className="suggestionsList">
{suggestions.slice(0, 10).map(s => ( //shows just 10 suggestions
<option className="suggestionsList_item" key={s.id} onClick={() => handleSuggestionClick(s.id)}>
{s.name}
</option>
))}
</datalist>
</div>
}`
And nothing worked.
Do you have any other solucion? Thanks!
Related
import ProductCard from "./ProductCard";
import "../Styles/Products.css";
import "../Styles/Filter.css";
import { v4 as uuidv4 } from "uuid";
const Products = (props) => {
const skins = props.skins;
// const [filteredSkins, setFilteredSkins] = useState();
const [gameFilter, setGameFilter] = useState("DEFAULT");
const [qualityFilter, setqualityFilter] = useState("DEFAULT");
let skinsObj = {};
let qualityObj = {};
for (let i = 0; i < skins.length; i++) {
skinsObj[skins[i].gameName] = i;
qualityObj[skins[i].quality] = i;
}
const setGame = (e) => {
setGameFilter(e.target.value);
console.log(gameFilter, qualityFilter);
};
const setquality = (e) => {
setqualityFilter(e.target.value);
console.log(gameFilter, qualityFilter);
};
console.log(gameFilter, qualityFilter);
return (
<React.Fragment>
<div className="filter_option">
<div className="filter_by_game Filter-Box">
<label htmlFor="games">Game : </label>
<select name="games" id="games" onChange={(e) => setGame(e)}>
<option value="DEFAULT">All</option>
{Object.keys(skinsObj).map((game) => {
return (
<option value={game} key={uuidv4()}>
{game}
</option>
//
);
})}
</select>
</div>
<div className="filter_by_quality Filter-Box">
<label htmlFor="quality">Quality : </label>
<select name="quality" id="quality" onChange={(e) => setquality(e)}>
<option value="all">All</option>
{Object.keys(qualityObj).map((quality) => {
return (
<option value={quality} key={uuidv4()}>
{quality}
</option>
//
);
})}
</select>
</div>
</div>
<div className="product-wrapper">
{skins &&
skins.map((skin) => {
return (
<ProductCard
key={uuidv4()}
className="product-list"
name={skin.name}
icon={skin.gameName}
price={skin.price}
quality={skin.quality}
picture={skin.picture}
/>
);
})}
</div>
</React.Fragment>
);
};
export default Products;
I need to click on any option from the list twice in a row to be applied in the box but the state change when I log it, also the second select affected when I change the other one both get reset to default try some solution but nothing help don't know if it is problem with the way of importing data or what
You currently don't set the value prop on <select>.
I have user with two parameters (username, and user groups )
I have Page that's update my user by changing username and update groups
it looks like:
Problem is , I can't highlight groups , I need to choose to update .
const AddUser = props =>{
let editing = false;
let initialUsername = "";
const[initialGroups, setInitialGroups] = useState([])
useEffect(()=>{
retrieveGroups();
},[])
const retrieveGroups = () => {
BackendService.getAllGroups()
.then(response => {
setInitialGroups(response.data);
})
.catch(e => {
console.log(e);
});
}
const[username, setUsername] = useState(initialUsername);
const[groups, setGroups] = useState(initialGroups);
const[submitted, setSubmitted] = useState(false);
const onChangeUsername = e => {
const username = e.target.value;
setUsername(username);
}
const onChangeGroups = e => {
console.log(e);
setGroups(Array.from(e.currentTarget.selectedOptions, (v) => v.value));
}
const saveUser = () => {
var data = {
username: username,
groups: groups,
complited: false,
}
BackendService.editUser(
props.location.state.currentUser.id,
data)
.then(response=>{
setSubmitted(true);
console.log(response.data)
})
.catch(e=>{
console.log(e);
})
)
.catch(e=>{
console.log(e);
});
}
}
return(
<Container>
{submitted ? (
<div>
<h4>
User Edited Successfully
</h4>
<Link to={"/users/"}></Link>
Back to Users
</div>
):(
<Form>
<Form.Group className="mb-3">
<Form.Label>
"Edit" User
</Form.Label>
<Form.Control
type="text"
required
value={username}
placeholder="Enter username here"
onChange={onChangeUsername}
/>
<Form.Control
as="select"
multiple value={initialGroups}
onChange={onChangeGroups}
>
{initialGroups.map(group => (
<option key={group.id} value={group.id}>
{group.name}
</option>
))}
</Form.Control>
</Form.Group>
<Button variant="info" onClick={saveUser}>
"Edit" User
</Button>
</Form>
)}
</Container>
)
}
export default AddUser;
In this section I get all groups(initialGroups) I have in database:
const[initialGroups, setInitialGroups] = useState([])
useEffect(()=>{
retrieveGroups();
},[])
const retrieveGroups = () => {
BackendService.getAllGroups()
.then(response => {
setInitialGroups(response.data);
})
.catch(e => {
console.log(e);
});
}
After I put InitialGroups in :
<Form.Control
as="select"
multiple value={initialGroups}
onChange={onChangeGroups}
>
{initialGroups.map(group => (
<option key={group.id} value={group.id}>
{group.name}
</option>
))}
</Form.Control>
And process in :
const onChangeGroups = e => {
console.log(e);
setGroups(Array.from(e.currentTarget.selectedOptions, (v) => v.value));
}
What I do wrong ? I can't highlight group I need to proces and update user
I would like to make the following suggestions:
You don't need to use 'initialGroups' as a state variable. You already have a 'groups' variable which can receive an initial value.
const [groups, setGroups] = useState([]);
You can directly set the 'groups' variable once you retrieve the data.
BackendService.getAllGroups()
.then(response => {
setGroups(response.data);
})
You should pass 'groups' to the 'value' prop on the Form component, instead of 'initialGroups' (and then map over it).
<Form.Control
as="select"
multiple
value={groups}
onChange={onChangeGroups}
>
{groups.map(group => (
<option key={group.id} value={group.id}>
{group.name}
</option>
))}
</Form.Control>
Hope this helps!
I use prop drilling to pass down the id value of the document, but every time I click on a document to update it using updateDoc, the same document gets updated(always the latest one added), not the one I clicked on. I don't understand why the unique IDs don't get passed down correctly to the function, or whether that's the problem. I use deleteDoc this way and it's working perfectly. Any help will be appreciated.
This is where I get the id value from
const getPosts = useCallback(async (id) => {
const data = await getDocs(postCollectionRef);
setPosts(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
});
useEffect(() => {
getPosts();
}, [deletePost]);
return (
<div className={classes.home}>
<ul className={classes.list}>
{posts.map((post) => (
<BlogCard
key={post.id}
id={post.id}
title={post.title}
image={post.image}
post={post.post}
date={post.date}
showModal={showModal}
setShowModal={setShowModal}
deletePost={() => {
deletePost(post.id);
}}
showUpdateModal={showUpdateModal}
setShowUpdateModal={setShowUpdateModal}
/>
))}
</ul>
</div>
);
This is where I pass through the id value to the update modal component for each document:
function BlogCard(props) {
const [postIsOpen, setPostIsOpen] = useState(false);
const modalHandler = () => {
props.setShowModal((prevState) => {
return (prevState = !prevState);
});
};
const updateModalHandler = () => {
props.setShowUpdateModal((prevState) => {
return (prevState = !prevState);
});
};
const handleView = () => {
setPostIsOpen((prevState) => {
return (prevState = !prevState);
});
};
return (
<>
{props.showUpdateModal && (
<UpdateModal
showUpdateModal={props.showUpdateModal}
setShowUpdateModal={props.setShowUpdateModal}
id={props.id}
title={props.title}
image={props.image}
post={props.post}
/>
)}
{props.showModal && (
<DeleteModal
showModal={props.showModal}
setShowModal={props.setShowModal}
deletePost={props.deletePost}
/>
)}
<div className={classes.blogCard} id={props.id}>
<div className={classes.head}>
<p className={classes.title}> {props.title}</p>
<div className={classes.buttons}>
<button className={classes.editButton} onClick={updateModalHandler}>
Edit
</button>
<button className={classes.removeButton} onClick={modalHandler}>
Delete
</button>
</div>
</div>
<p className={classes.date}>{props.date}</p>
<img src={props.image} alt="image" />
{!postIsOpen ? (
<p className={classes.viewHandler} onClick={handleView}>
Show More
</p>
) : (
<p className={classes.viewHandler} onClick={handleView}>
Show Less
</p>
)}
{postIsOpen && <p className={classes.article}>{props.post}</p>}
</div>
</>
);
}
export default BlogCard;
Here I create the function to update and add the onclick listener
function UpdateModal(props) {
const [title, setTitle] = useState(props.title);
const [image, setImage] = useState(props.image);
const [post, setPost] = useState(props.post);
const updateModalHandler = (prevState) => {
props.setShowUpdateModal((prevState = !prevState));
};
const updatePost = async (id) => {
const postDocRef = doc(db, "posts", id);
props.setShowUpdateModal(false);
try {
await updateDoc(postDocRef, {
title: title,
image: image,
post: post,
});
} catch (err) {
alert(err);
}
};
return (
<div onClick={updateModalHandler} className={classes.backdrop}>
<form onClick={(e) => e.stopPropagation()} className={classes.form}>
<label htmlFor="title">Title</label>
<input
id="title"
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<label htmlFor="image">Image(URL)</label>
<input
id="image"
type="text"
value={image}
onChange={(e) => setImage(e.target.value)}
/>
<label htmlFor="post">Post</label>
<textarea
id="post"
cols="30"
rows="30"
value={post}
onChange={(e) => setPost(e.target.value)}
/>
<div className={classes.buttons}>
<button className={classes.cancel} onClick={updateModalHandler}>Cancel</button>
<button className={classes.update} onClick={() => updatePost(props.id)}>Update</button>
</div>
</form>
</div>
);
}
export default UpdateModal;
This is the way my data is structured
firebase
I'm trying to build a CV builder which the user can input information such as the company, date, title, location and it gets saved inside an array when the user presses the save button. Then, that array is rendered in HTML form with a remove button.
I want that remove button to only delete one item of an array. For example, if we create two companies I've worked on it would create a remove button for both of them
I want that when we click the remove button once, not all the items of the array gets deleted which is what my code is currently doing. I can't figure out a logic to perform this and I've only tried this filter method but it deletes all of the items...
import React, { useState } from "react";
import '../styles/style.css'
const Experience = (props) => {
const [toggle, setToggle] = useState(false);
const [input, setInput] = useState({title: "", company: "", date: "", location: "", description: ""})
const [result, setResult] = useState([])
const togglePopup = () => {
setToggle(!toggle);
}
const saveForm = (e) => {
e.preventDefault()
setResult(result.concat(input))
}
const removeItems = (data) => {
setResult(result.filter(todo => todo.title === data.title));
}
console.log(result)
return(
<ul>
<button onClick={togglePopup}>Work Experience</button>
{result.map((data) => (
<>
<p key={data.title}>{data.title}</p>
<p>{data.company}</p>
<p>{data.date}</p>
<p>{data.location}</p>
<p>{data.description}</p>
<button onClick={removeItems}>Remove</button>
</>
))}
{toggle && <form>
<div>
<label>Job Title</label>
</div>
<div>
<input value={input.title} onChange={ e => {setInput({ title: e.target.value }) }}/>
</div>
<div>
<label>Company</label>
</div>
<div>
<input onChange={ e => {setInput({...input, company: e.target.value }) }}/>
</div>
<div>
<label>Date Worked (MM/YYYY - MM/YYYY)</label>
</div>
<div>
<input onChange={ e => {setInput({...input, date: e.target.value }) }}/>
</div>
<div>
<label>Location (e.g. Los Angeles, CA)</label>
</div>
<div>
<input onChange={ e => {setInput({...input, location: e.target.value }) }}/>
</div>
<div>
<label>Description</label>
</div>
<div>
<input onChange={ e => {setInput({...input, description: e.target.value }) }}/>
</div>
<button onClick={saveForm}>Save</button>
<button onClick={togglePopup}>Cancel</button>
</form>}
</ul>
)
}
export default Experience
You need to pass particular object to match and remove like-
<button onClick={() => removeItems(data)}>Remove</button>
Improved:
{result.map((data) => (
<>
<p key={data.title}>{data.title}</p>
<p>{data.company}</p>
<p>{data.date}</p>
<p>{data.location}</p>
<p>{data.description}</p>
<button onClick={() => removeItems(data)}>Remove</button>
</>
))}
function -> remove particular which is clicked.
const removeItems = (data) => {
const updated = result.filter(todo => todo.title !== data.title)
setResult([...updated]);
}
I hope anyone can help me! I cant render my object in the right way.
My JSON:
{"status":"Entregado","_id":"5f490dd9b3f5192230a8f536","products":[{"_id":"5f44eaa1cf215b305449e216","name":"Reto Gratuito Medita Healing","price":0,"createdAt":"2020-08-25T10:40:33.845Z","updatedAt":"2020-08-25T10:40:33.845Z","__v":0,"count":1}],"details":"dimebag_666","client_email":"jobroman83#gmail.com","state":"CDMX","address":"acueducto","zip":"16200","client_id":"5f2ac527543c3835d028337c","amount":0,"createdAt":"2020-08-28T13:59:53.129Z","updatedAt":"2020-09-07T09:03:39.117Z","__v":0}
I rendered almost all the component with this code:
const SingleOrder = (props) => {
const token = getCookie('token') //// <-- right one
const Id = getCookie('token')
const [order, setOrder] = useState({});
const [error, setError] = useState(false);
const [statusValues, setStatusValues] = useState([])
const loadSingleOrder = orderId => {
read(orderId,token).then( data => {
if (data.error){
setError(data.error)
} else {
setOrder(data)
}
})
}
const loadStatusValues = () => {
getStatusValues(Id, token).then(data => {
if (data.error){
console.log(data.error)
} else{
setStatusValues(data)
}
})
}
const handleStatusChange = (e, orderId) => {
// console.log('update order status')
updateOrderStatus(Id, token ,orderId, e.target.value).then(
data => {
if (data.error){
console.log('status update failed')
} else {
// setRun(!run)
loadSingleOrder(orderId)
console.log('changed')
alert('Has Cambiado el estatus')
}
}
)
}
const showStatus = (order) => {
return (
<div className='form-group'>
<h5 className='mark mb-4'>Estatus: {order.status}</h5>
<select className='form-control'
onChange={(e) => handleStatusChange(e, order._id)}>
<option>Actualizar estado de orden</option>
{statusValues.map((status, index) => (
<option key={index} value={status}>{status}</option>
) )}
</select>
</div>
)
}
const showInput = (key, value) => {
return (
<div className="input-group mb-2 mr-sm-2">
<div className="input-group-prepend">
<div className="input-group-text">{key}</div>
</div>
<input type="text" value={value} className="form-control" readOnly/>
</div>
)
}
useEffect (() => {
const orderId = props.match.params.orderId
loadSingleOrder(orderId)
loadStatusValues()
},[props])
const showOrderList = () => {
return (
<div className="card mr-2 mt-2 mb-5">
<h5 className="mb-1 mt-2 text-center bg-primary" style={{color:'red'}}>Numero de Orden: {order._id}</h5>
<ul className="list-group">
<li className='list-group-item'>
{/* {o.status} */}
{showStatus(order)}
</li>
<li className='list-group-item'>ID cliente: {order.client_id} </li>
<li className="list-group-item">Télefono: {order.client_phone}</li>
<li className="list-group-item" style={{fontWeight:'bold'}}> Cuenta de <i class="fab fa-instagram"/> vinculada al Taller: {order.details}</li>
<li className="list-group-item">E-mail: {order.client_email}</li>
<li className='list-group-item'>Total de la orden: ${order.amount}</li>
<li className='list-group-item' style={{fontWeight:'bold'}}>Comprado el dia:{" "}
{moment(order.createdAt).locale('es').format('LL')}
</li>
<li className='list-group-item'>Dirección : {order.address}</li>
<li className='list-group-item'>Estado : {order.state}</li>
<li className='list-group-item'>Código Postal: {order.zip}</li>
if a put this code the problem is --> "TypeError: order.products is undefined" {order.products.map((p, pIndex) => (
<div className='' key={pIndex} style={{padding:'20px', border:'1px solid #e6c4ba'}}
>
{showInput('Nombre del producto:', p.name)}
{/* {showInput('Precio del producto $:', p.price)}
{showInput('Cantidad pedida del producto:', p.count)}
{showInput('ID del producto:', p._id)} */}
</div>
))}
if i only put this i recived --> {order.products}
Error: Objects are not valid as a React child (found: object with keys {_id, name, price, createdAt, updatedAt, __v, count}). If you meant to render a collection of children, use an array instead.
</ul>
</div>
)
}
return(
<Layout>
<div className='container'>
{/* {showOrderList()} */}
{JSON.stringify(order)}
</div>
</Layout>
)
}
But when i want to render the products inside of my object i cant Render them, its because of that i receiving the message :
Error: Objects are not valid as a React child (found: object with keys
{_id, name, price, createdAt, updatedAt, __v, count}). If you meant to
render a collection of children, use an array instead.
{order.products}
I have to render this in the right way, I tried with map etc.. I'm always getting this error.
You can use map() like this:
<ul>
{order?.products.map(item => (
<>
<li>{item.name}</li>
<li>{item.price}</li>
<li>{new Date(item.createdAt)}</li>
<li>{new Date(item.updatedAt)}</li>
</>
)
}
</ul>
you can also include the rest of the object items like id or count if necessary.
using <> </> tags are a short way of <React.Fragment> </React.Fragment> respectively.