I would like to pass several data in a modal. My modal works fine but does not display the data I want. I would like to display the data BY ID, but I get all the data. This is my code :
App.js :
import React, { useState, useEffect } from "react";
import Modal from "./Modal";
import PlayCircleIcon from '#mui/icons-material/PlayCircle';
const App = (props) => {
const [image, setImage] = useState([])
return (
<div>
{image.map((image) => {
return (
<div key={image.id}>
<img src={...}
alt={image.title}
/>
<ExpandMoreIcon onClick={handleClick} />
</div>
)
})}
</div>
);
}
export default App;
Modal.js :
import React from 'react';
const Modal = ({ showModal, image }) => {
console.log("result", image);
return (
<div className="modal" >
<div className='modal__content'>
<h1 className='modal__title'>{image.title}</h1>
<p className='modal__description'>{image.description}</h1>
</div>
</div>
);
}
export default Modal;
I think the problem comes from here image={image} in App.js because I get 8 tables in console.log("result", movie);
{showModal && <Modal showModal={handleClick} image={image} />}
Your problem is your modal state is true/false value, so that's why once you trigger showModal, it will show all modals according to your number of images
The fix can be
Note that I modified your state name to align with new state values
const [modalImage, setModalImage] = useState(); //undefined as no image selected
const handleClick = (imageId) => {
setModalImage(imageId);
};
Your elements will be like below
<ExpandMoreIcon onClick={() => handleClick(image.id)} />
{modalImage === image.id && <Modal showModal={() => handleClick(image.id)} image={image} />}
If you want to close the modal, you can reset modalImage state
onClick={() => handleClick(undefined)}
I just noticed that you also can move your modal out of the image loop, and potentially you can pass the entire image object to the modal too
The full implementation
import React, { useState, useEffect } from "react";
import Modal from "./Modal";
import PlayCircleIcon from '#mui/icons-material/PlayCircle';
const App = (props) => {
const [image, setImage] = useState([])
const [modalImage, setModalImage] = useState(); //no image selected
const handleClick = (image) => {
setModalImage(image);
};
useEffect(() => {
...
}, []);
return (
<div>
{image.map((image) => {
return (
<div key={image.id}>
<img src={...}
alt={image.title}
/>
<ExpandMoreIcon onClick={() => handleClick(image)} />
</div>
)
})}
{modalImage && <Modal showModal={() => handleClick(modalImage)} image={modalImage} />}
</div>
);
}
export default App;
Both the map item and the image state array are named image in your code here;
image.map((image) => {
/// your code
})
which could be the ambiguity that led to showing the whole array of data instead of one image in the array.
Related
By using console.log(responseData.places) I have checked the fetching works since I am using a hook for this and seems to work fine until I setLoadedPlaces with is the method I use to update the loadedPlaces which I later use to get the values to fill the frontend part of the website.
This is the output I get from this console.log I did and the values are correct.
[{…}]
0: address: "sis se puede
busrespect: 'tu puedes',
creator: "6384e2f543f63be1c560effa"
description: "al mundial"
id: "6384e30243f63be1c560f000"
image:"https://upload.wikimedia.org/wikipedia/commons/thumb/1/10/Empire_State_Building_%28aerial_view%29.jpg/400px-Empire_State_Building_%28aerial_view%29.jpg"location: {lat: -12.086158, lng: -76.898019}
title: "Peru"
__v: 0
_id: "6384e30243f63be1c560f000"[[Prototype]]:
Objectlength: 1[[Prototype]]: Array(0)
So after this this the code I have in the frontend (SINCE the backend works properly) Let me know if you have any doubts with this logic
This is UserPlaces.js
import React, {useState, useEffect } from 'react';
import PlaceList from '../components/PlaceList';
import { useParams } from 'react-router-dom';
import { useHttpClient } from '../../shared/hooks/http-hook';
import ErrorModal from '../../shared/components/UIElements/ErrorModal';
import LoadingSpinner from '../../shared/components/UIElements/LoadingSpinner';
const UserPlaces = () => {
const {loadedPlaces, setLoadedPlaces} = useState();
const {isLoading, error, sendRequest, clearError } = useHttpClient();
const userId = useParams().userId;
useEffect(() => {
const fetchPlaces = async () => {
try {
const responseData = await sendRequest(
`http://localhost:5000/api/places/user/${userId}`
);
console.log(responseData.bus_stops)
setLoadedPlaces(responseData.bus_stops);
} catch (err) {}
};
fetchPlaces();
}, [sendRequest, userId]);
return (
<React.Fragment>
<ErrorModal error={error} onClear={clearError} />
{isLoading && (
<div className="center">
<LoadingSpinner />
</div>
)}
{!isLoading && loadedPlaces && <PlaceList items={loadedPlaces} />}
</React.Fragment>
);
};
export default UserPlaces;
This is Place-List.js
import React from 'react';
import "./PlaceList.css"
import Card from '../../shared/components/UIElements/Card'
import PlaceItem from './PlaceItem';
import Button from '../../shared/components/FormElements/Button';
const PlaceList = props => {
if (props.items.length === 0) {
return (
<div className='place-list-center'>
<Card>
<h2>No bus stops available. Be the first one to create one!</h2>
<Button to='/places/new'> Create Bus Stop </Button>
</Card>
</div>
);
}
return (
<ul className="place-list">
{props.items.map(bus_stops => (
<PlaceItem
key={bus_stops.id}
id={bus_stops.id}
image={bus_stops.image}
title={bus_stops.title}
busrespect={bus_stops.busrespect}
description={bus_stops.description}
address={bus_stops.address}
creatorId={bus_stops.creator}
coordinates={bus_stops.location}
/>
))}
</ul>
);
};
export default PlaceList;
This is PlaceItem.js
import React, { useState } from 'react';
import { useContext } from 'react';
import Card from '../../shared/components/UIElements/Card';
import Button from '../../shared/components/FormElements/Button';
import Modal from '../../shared/components/UIElements/Modal';
import Map from '../../shared/components/UIElements/Map';
import {AuthContext} from '../../shared//context/auth-context'
import "./PlaceItem.css";
const PlaceItem = props => {
const auth = useContext(AuthContext);
const [showMap, setShowMap] = useState(false);
const [showConfirmModal, setShowConfirmModal] = useState(false);
const openMapHandler = () => setShowMap(true);
const closeMapHandler = () => setShowMap(false);
const showDeleteWarningHandler = () => {
setShowConfirmModal(true);
};
const cancelDeleteHandler = () => {
setShowConfirmModal(false);
};
const confirmDeleteHandler = () => {
setShowConfirmModal(false); //when clicked close the new Modal
console.log('DELETING...');
};
return (
<React.Fragment>
<Modal show={showMap}
onCancel={closeMapHandler}
header={props.address}
contentClass="place-item__modal-content"
footerClass="place-item__modal-actions"
footer={<Button onClick={closeMapHandler}>Close </Button>}
>
<div className='map-container'>
<Map center={props.coordinates} zoom={16}/> {/* Should be props.coordinates but we writing default data for now until geocoding solved. */}
</div>
</Modal>
<Modal
show={showConfirmModal}
onCancel={cancelDeleteHandler}
header="Are you entirely sure?"
footerClass="place-item__modal-actions"
footer={
<React.Fragment>
<Button inverse onClick={cancelDeleteHandler}>
CANCEL
</Button>
<Button danger onClick={confirmDeleteHandler}>
DELETE
</Button>
</React.Fragment>
}
>
<p>
Do you want to proceed and delete this place? Please note that it
can't be undone thereafter.
</p>
</Modal>
<li className='"place=item'>
<Card className="place-item__content">
<div className='place-item__image'>
<img src={props.image} alt={props.title}/>
</div>
<div className='place-item__info'>
<h2>{props.title}</h2>
<h3>{props.address}</h3>
<p>{props.description}</p>
<p>{props.busrespect}</p>
</div>
<div className='place-item__actions'>
<Button inverse onClick={openMapHandler}> VIEW ON MAP</Button>
{auth.isLoggedIn && (<Button to={`/places/${props.id}`}> EDIT</Button> )}
{auth.isLoggedIn &&<Button danger onClick={showDeleteWarningHandler}> DELETE </Button>}
</div>
</Card>
</li>
</React.Fragment>
);
};
export default PlaceItem;
This is auth-context:
import { createContext } from "react";
export const AuthContext = createContext({
isLoggedIn: false,
userId: null,
login: () => {},
logout: () => {}});
This is is Modal.js
import React from 'react';
import ReactDOM from 'react-dom';
import Backdrop from './Backdrop';
import { CSSTransition } from 'react-transition-group';
import './Modal.css';
const ModalOverlay = props => {
const content =(
<div className={`modal ${props.className}`} style = {props.style}>
<header className={`modal__header ${props.headerClass}`}>
<h2>{props.header}</h2>
</header>
<form
onSubmit={
props.onSubmit ? props.onSubmit : event => event.preventDefault()
}
>
<div className={`modal__content ${props.contentClass}`}>
{props.children}
</div>
<footer className={`modal__content ${props.footerClass}`}>
{props.footer}
</footer>
</form>
</div>
);
return ReactDOM.createPortal(content, document.getElementById('modal-hook'));
};
const Modal = props => {
return (
<React.Fragment>
{props.show && <Backdrop onClick={props.onCancel} />}
<CSSTransition in={props.show}
mountOnEnter
unmountOnExit
timeout={200}
classNames="modal"
>
<ModalOverlay {...props}/>
</CSSTransition>
</React.Fragment>
);
};
export default Modal;
Also Trust the routing is correct since I have checked it already and I am just wondering if the logic in REACT with loadedPlaces, PlaceItema and PlaceList makes sense and it working. Let me know please. It will be really helpful.
Summary: Not getting any error but no visual data appears in the scren just the header of my website and the background (rest is empty) even though logic is functional.
const {loadedPlaces, setLoadedPlaces} = useState();
change the above line to
const [loadedPlaces, setLoadedPlaces] = useState();
I'm trying to create an edit feature to my todo-list but i'm kind of stuck and receiving a weird behaviour.
I'm filtering the array using the id's but what happens is that the entire array is changing instead of one element inside of it.
What supposed to happen is when clicking the edit button, the element im clicking on should change to an input (not the entire array)
thanks for any kind of help!
App:
import React, { useState } from "react";
import Header from "./UI/Header";
import TodoList from "./Components/TodoList";
import AddTodo from "./Components/AddTodo";
import { v4 as uuidv4 } from "uuid";
function App() {
const [todos, setTodos] = useState([]);
const [editTodo, setEditTodo] = useState(false);
const onAddHandler = (text) => {
setTodos([
...todos,
{
name: text,
id: uuidv4(),
},
]);
};
const deleteTodoHandler = (id) => {
setTodos(todos.filter((todo) => todo.id !== id));
};
const editTodoHandler = (id) => {
todos.filter((todo) => todo.id === id);
setEditTodo(!editTodo);
};
return (
<div>
<div className="App">
<AddTodo onAddHandler={onAddHandler} />
<Header />
<TodoList
todos={todos}
deleteTodoHandler={deleteTodoHandler}
editTodoHandler={editTodoHandler}
editTodo={editTodo}
/>
</div>
</div>
);
}
export default App;
TodoList.js :
import React, { useState } from "react";
import Todo from "./Todo";
const TodoList = (props) => {
return (
<Todo todo={props.todo}>
{props.todos.map((todo) => {
return (
<p>
{props.editTodo ? <input /> : <span>{todo.name}</span>}
<button onClick={() => props.deleteTodoHandler(todo.id)}>
Delete
</button>
<button onClick={() => props.editTodoHandler(todo.id)}>Edit</button>
</p>
);
})}
</Todo>
);
};
export default TodoList;
When you set the editTodo property to true, the TodoList component re-renders and loops through the todo array again, changing every <span> to an <input>. You're going to have to pass the id of the todo that you want to edit, and add a condition to only change that single item to an <input>.
I have a page that displays images fetched with an api call using the map method. Now what I want to do is to toggle the like or unlike state of FavoriteIcon by clicking it.
When I try to implement that using the useState hook, if I click on the FavoriteIcon in a single image, all the images are set to the same state. I want only the clicked image to change its state.
import React, { useState, useEffect } from "react";
import FavoriteIcon from "#material-ui/icons/Favorite";
import FavoriteBorderIcon from "#material-ui/icons/FavoriteBorder";
// services
import medias from "../../services/mediaServices";
function Favorites({ fetchUrl, catagory }) {
const classes = useStyles();
const [images, setImages] = useState([{}]);
const [favIcon, setFavIcon] = useState();
useEffect(() => {
async function getData() {
const request = await medias.getMedias(fetchUrl);
setImages(request.data.message);
}
getData();
}, [fetchUrl, enqueueSnackbar, closeSnackbar]);
return (
<div className={classes.favorites}>
<div className={classes.favorites__items}>
{images &&
images.map(image => {
return (
<div
elevation={3}
className={classes.favorites__posters}
style={
{
// border: "2px solid black"
}
}
>
<img
key={image.id}
src={image.thumbnailUrl}
alt={image.title}
/>
{favIcon ? (
<FavoriteIcon onClick={() => setFavIcon(false)} />
) : (
<FavoriteBorderIcon onClick={() => setFavIcon(true)} />
)}
</div>
);
})}
</div>
</div>
);
}
export default Favorites;
Without breaking it into more components, your favIcon state needs to contain the state of all your icons. It should be initialized with something like
images.map(image => {id: image.id, state: false})
and then you could change your onClick to just update the relevant part of state
setFavIcon([...favIcon].map(x => {if(x.id === image.id) {x.state = !x.state}; return x}))
I am new to React and trying to display list of data with checkbox and inputbox. In details, I want to grab a series of data from database and put each record into a <div> like element with checkbox and inputbox. So I can check the record and change the data and then do the re-save action after clicking a button. Since the number of data will keep changing, how to make it dynamic? Also, how can I mark down which records are being checked and need to be saved? Thanks!
Code:
App.js:
import React from 'react';
import { useState, useEffect } from 'react';
import { Menu, Message, Button, Segment } from 'semantic-ui-react';
import SemanticDatepicker from 'react-semantic-ui-datepickers';
import 'react-semantic-ui-datepickers/dist/react-semantic-ui-datepickers.css';
import Form from './Form';
export default function App(props){
const [currentDate, setNewDate] = useState(null);
const onChange = (event, data) => setNewDate(data.value);
const loadData= (event) => {
return (<Form date = {currentDate} />);
};
return (
<div className="App">
<div>
<Menu borderless>
<Menu.Item >
<div >
<img src={logo} alt="image" />
</div>
</Menu.Item>
</Menu>
<Segment>
<SemanticDatepicker onChange={onChange} />
<Button onClick={loadData}>Load Data</Button>
</Segment>
<Segment>>
</Segment>
//Here will diaplyed returned list of data after click button
</div>
</div>
)
};
Simple JSON response:
{
"APPLE":{
"PRICE":100
},
"ORANGE":{
"PRICE":20
},
"PEAR":{
"PRICE":10
}
}
You could use your data to build your form.
You need to build the state from your data.
Also, map your input fields with respect to your state.
If the state needs different input fields, you could define your input fields in deriveStateFromData.
You can check the example here
For Object.keys, you could check the docs here
import React from 'react';
const price = {
"APPLE":{
"PRICE":100
},
"ORANGE":{
"PRICE":20
},
"PEAR":{
"PRICE":10
}
}
function deriveStateFromData(data) {
let state = {}
Object.keys(data).forEach(key => {
state[key] = data[key]['PRICE']
})
return state;
}
function MyForm({ data }) {
const [form, setFormData] = React.useState(deriveStateFromData(data));
const handleChange = e => {
setFormData({ ...form, [e.target.name]: Number(e.target.value) });
}
console.log(form)
return (
<>
{Object.keys(form).map(key => {
return (
<div>
<label>{key}</label>
<input
name={key}
value={form[key]}
onChange={handleChange}
/>
</div>
)
})}
</>
)
}
const App = () => <MyForm data={price} />
export default App;
On function call i was changing the image style using useState hooks
I was sending these property as an props
basically i want to a function which should contain style property for img and pass it to another component as propsstyle = {{opacity: ".3"}}
import React, { useState } from 'react';
import BackgroundImage from '../Image/Homepage/background.png'
const HomePage = () => {
const [modalShow, setModalShow] = useState(false);
const [image, setImage] = useState(BackgroundImage)
return (
<div>
<img src={image} className="first-image" alt="backGroundImage" />
</div>
<Modals
show={modalShow}
onhide={() => setModalShow(false)}
sendImages = {() => setImage( style = {{opacity: ".3"}} )}
/>
)}
this is throwing an error
sendImages = {() => setImage( style = {{opacity: ".3"}} )}
I think this not right approach
It looks like you want to make opacity dynamic, instead you manipulate image src...
import React, { useState } from 'react';
import BackgroundImage from '../Image/Homepage/background.png'
const HomePage = () => {
const [modalShow, setModalShow] = useState(false);
const [image, setImage] = useState(BackgroundImage);
const [opacity, setOpacity] = useState(1);
return (
<>
<div>
<img src={image} className="first-image" style={{opacity}} alt="backGroundImage" />
</div>
<Modals
show={modalShow}
onhide={() => setModalShow(false)}
sendImages = {() => setOpacity(0.3)}
/>
</>
)}
If you only wish to update the style property on function call, you must store the style property in state and not the image. Also the syntax for setImage is incorrect in your code
import React, { useState } from 'react';
import BackgroundImage from '../Image/Homepage/background.png'
const HomePage = () => {
const [modalShow, setModalShow] = useState(false);
const [imageStyle, setImageStyle] = useState({})
return (
<>
<div>
<img src={BackgroundImage} style={imageStyle} className="first-image" alt="backGroundImage" />
</div>
<Modals
show={modalShow}
onhide={() => setModalShow(false)}
sendImages = {() => setImageStyle({opacity: ".3"})}
/>
</>
)}
NOTE: Also please note that state updaters with hooks do not merge the values but override it. So if you wish to update only certain properties make use of state updater callback method to return the merged values yourself