Firebase - set() doesn't change the value, creates new doc instead - reactjs

I'm trying to create read / unread chat option that will set the read value to true if opened.
When I do click the chat it opens the right db entry but doesn't set the value of read, instead it creates a new doc in my collection with read:true.
import {Avatar} from "#material-ui/core";
import StopRoundedIcon from "#material-ui/icons/StopRounded"
import "./Chat.css";
import ReactTimeago from "react-timeago";
import {selectImage} from "./features/appSlice";
import {useDispatch} from "react-redux";
import {db} from "./firebase";
import {useHistory} from "react-router-dom";
function Chat({id, username, timestamp, read, imageUrl, profilePic}) {
const dispatch = useDispatch();
const history = useHistory();
const open = () => {
if(!read) {
dispatch(selectImage(imageUrl));
db.collection("posts").doc(id).set(
{
read:true,
},
{merge:true}
);
history.push('/chats/view');
}
};
return (
<div onClick={open} className="chat">
<Avatar className="chat__avatar" src={profilePic} />
<div className="chat__info">
<h4>{username}</h4>
<p>Tap to view - <ReactTimeago date={new Date(timestamp?.toDate()).toUTCString()} /></p>
</div>
{!read && <StopRoundedIcon className="chat__readIcon" />}
</div>
)
}
export default Chat
Chat shouldn't be available for preview if the read value is true.
Preview that adds values to collection:
import {useSelector, useDispatch} from "react-redux";
import { useHistory } from "react-router-dom";
import {useEffect} from "react";
import {selectCameraImage, resetCameraImage} from "./features/cameraSlice";
import "./Preview.css";
import CloseIcon from "#material-ui/icons/Close";import TextFieldsIcon from '#material-ui/icons/TextFields';import CreateIcon from '#material-ui/icons/Create';import NoteIcon from '#material-ui/icons/Note';import MusicNoteIcon from '#material-ui/icons/MusicNote';import AttachFileIcon from '#material-ui/icons/AttachFile';import CropIcon from '#material-ui/icons/Crop';import TimerIcon from '#material-ui/icons/Timer';import SendIcon from '#material-ui/icons/Send';
import {v4 as uuid} from "uuid";
import {storage, db} from "./firebase";
import firebase from "firebase";
function Preview() {
const cameraImage = useSelector(selectCameraImage);
const history = useHistory();
const dispatch = useDispatch();
useEffect(() => {
if(!cameraImage) {
history.replace('/')
}
}, [cameraImage, history]);
const closePreview = () => {
dispatch(resetCameraImage());
};
const sendPost = () => {
const id = uuid();
const uploadTask = storage.ref(`posts/${id}`).putString(cameraImage, "data_url");
uploadTask.on('state_changed', null, (error) => {
console.log(error)
},
() => {
storage.ref('posts').child(id).getDownloadURL().then((url) => {
db.collection('posts').add({
imageUrl:url,
username:'Egon',
read:false,
//profilePic
timestamp:firebase.firestore.FieldValue.serverTimestamp(),
});
history.replace('/chats');
});
});
};
return (
<div className="preview">
<CloseIcon className="preview__close" onClick={closePreview} />
<div className="preview__toolbarRight">
<TextFieldsIcon />
<CreateIcon />
<NoteIcon />
<MusicNoteIcon />
<AttachFileIcon />
<CropIcon />
<TimerIcon />
</div>
<img src={cameraImage} alt="" />
<div onClick={sendPost} className="preview__footer">
<h2>Send Now</h2>
<SendIcon fontSize="small" className="preview__sendIcon" />
</div>
</div>
)
}
export default Preview
and Chats component that calls Chat function and passes id:
import {useState, useEffect} from "react";
import {Avatar} from "#material-ui/core";
import "./Chats.css";
import SearchIcon from "#material-ui/icons/Search";
import ChatBubbleIcon from "#material-ui/icons/ChatBubble";
import {db} from "./firebase";
import Chat from "./Chat";
function Chats() {
const [posts, setPosts] = useState([]);
useEffect(() => {
db.collection('posts').orderBy('timestamp', 'desc').onSnapshot(snapshot => setPosts(snapshot.docs.map(doc => ({
id:doc.id,
data:doc.data(),
}))))
}, [])
return (
<div className="chats">
<div className="chats__header">
<Avatar className="chats__avatar" />
<div className="chats__search">
<SearchIcon />
<input placeholder="Friends" type="text" />
</div>
<ChatBubbleIcon className="chats__chatIcon" />
</div>
<div className="chat__posts">
{posts.map(({id, data: {profilePic, username, timestamp, imageUrl, read}}) => (
<Chat key={id} username={username} timestamp={timestamp} imageUrl={imageUrl} read={read} profilePic={profilePic} />
))}
</div>
</div>
)
}
export default Chats

Found the solution, I forgot to add id in Chat component while maping them.
<div className="chat__posts">
{posts.map(({id, data: {profilePic, username, timestamp, imageUrl, read}}) => (
<Chat key={id} id={id] username={username} timestamp={timestamp} imageUrl={imageUrl} read={read} profilePic={profilePic} />
))}
</div>

Related

My buttons dont work when i started my project on first load. Please helpme

`I am doing a spending money application. When i start my project by npm run start or refreshin page , my buttons dont work on first load. But if i do new changes and save but dont refresh the page or restart by npm run start , my buttons works. what is the reason ? i cant understand...
My app is like this :
My index.js:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
My app.js:
import ProductItem from './components/ProductItem';
import ShoppingCart from './components/ShoppingCart';
import MainContext from "./MainContext"
import './index.css';
import {data} from "./data.js"
import { useEffect, useState } from 'react';
function App() {
const products= [...data]
const [cartItems, setCartItems] = useState([])
const [cartTotal, setCartTotal] = useState(0)
const [mainMoney, setMainMoney] =useState(10000)
const context= {
products,
cartItems,
setCartItems,
cartTotal,
setCartTotal,
mainMoney,
setMainMoney,
}
return (
<MainContext.Provider value={context}>
<div className="app-container">
<div className='money-header'> Harcayacak <span> ₺ {mainMoney}</span> paranız kaldı!</div>
<div className='inner-container'>
{
products.map( item => <ProductItem item={item}/>)
}
</div>
<ShoppingCart/>
</div>
</MainContext.Provider>
);
}
export default App;
My MainContext.js
import { createContext } from "react";
const MainContext = createContext()
export default MainContext
My ShoppingCart.jsx component:
import React, { useContext } from 'react'
import MainContext from '../MainContext'
const ShoppingCart = () => {
let {
cartTotal,
setCartTotal,
cartItems,
setCartItems,
mainMoney,
setMainMoney } = useContext(MainContext)
const sepetiSifirla = () => {
console.log("sepet sifirlandii ")
}
return (
<div className='shopping-cart-container'>
<h3>Alışveriş Detayları</h3>
<div className='cart-item'>
<p>Cart Item <span className='item-quantity'> x1</span></p>
</div>
<hr/>
<div className='cart-footer'>
<button className='sepeti-sifirla' onClick={sepetiSifirla}>Sepeti Sıfırla</button>
<h3 className='cart-total'>Toplam: ₺ {cartTotal}</h3>
</div>
</div>
)
}
export default ShoppingCart
my ProductItem.jsx component:
import React, { useContext } from 'react'
import MainContext from '../MainContext.js'
const ProductItem = ({item}) => {
let {
cartItems,
setCartItems,
mainMoney,
setMainMoney } = useContext(MainContext)
const sat = (item) => {
console.log("sat",item)
}
const satinAl = (item) => {
console.log("satin al ",item)
}
return (
<div className='product-item'>
<div className='product-img'>
<img src={require(`${item.url}`)} alt={item.name} />
</div>
<h3> {item.name} </h3>
<p className='product-price'> ₺ {` ${item.price}`} </p>
<div className='product-buttons'>
<button className='sat-btn' onClick={() => sat(item)}>Sat</button>
<h4>{item.quantity}</h4>
<button className='satin-al-btn' onClick={() => satinAl(item)}>Satın Al</button>
</div>
</div>
)
}
export default ProductItem

In react Firebase Chat App i have afirst issue with closed Socket and a second with redirection after signin with google

My 4 js files are the following.i use react router v6 and after the signin in useEffect tried to redirect in chats page.
import "./App.css";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Login from "./components/Login";
import Chats from "./components/Chats";
import { AuthProvider } from "./contexts/AuthContext";
function App() {
return (
<div style={{ fontFamily: "Avenir" }}>
<Router>
<AuthProvider>
<Routes>
<Route path="/" element={<Login />} />
<Route path="/chats" element={<Chats />} />
</Routes>
</AuthProvider>
</Router>
</div>
);
}
export default App;
import React from "react";
import { useNavigate } from "react-router-dom";
import { Col, Row } from "react-grid-system";
import {
ChatEngineWrapper,
Socket,
ChatList,
ChatFeed,
ChatSettings,
} from "react-chat-engine";
import { auth } from "../firebase";
import { useAuth } from "../contexts/AuthContext";
const Chats = () => {
const navigate = useNavigate();
const { user } = useAuth();
console.log(user);
const handleLogout = async () => {
await auth.signOut();
navigate("/", { replace: true });
};
return (
<div className="chats-page">
<div className="nav-bar">
<div className="logo-tab">TotalChat</div>
<div onClick={handleLogout} className="logout-tab">
Logout
</div>
</div>
<ChatEngineWrapper height="calc(100vh - 66px)">
<Socket
projectID={process.env.REACT_APP_PROJECT_ID}
userName={process.env.REACT_APP_USERNAME}
userSecret={process.env.REACT_APP_USER_SECRET}
/>
<Row>
<Col xs={0} sm={3}>
<ChatList />
</Col>
<Col xs={12} sm={6}>
<ChatFeed />
</Col>
<Col xs={0} sm={3}>
<ChatSettings />
</Col>
</Row>
</ChatEngineWrapper>
</div>
);
};
export default Chats;
import React from "react";
import { GoogleOutlined, FacebookOutlined } from "#ant-design/icons";
import { auth } from "../firebase";
import firebase from "firebase/compat/app";
const Login = () => {
return (
<div id="login-page">
<div id="login-card">
<h2>Welcome To Total Chat!</h2>
<div
className="login-button google"
onClick={() =>
auth.signInWithRedirect(new firebase.auth.GoogleAuthProvider())
}
>
<GoogleOutlined /> Sign In with Google
</div>
<br />
<br />
<div
className="login-button facebook"
onClick={() =>
auth.signInWithRedirect(new firebase.auth.FacebookAuthProvider())
}
>
<FacebookOutlined /> Sign In with Facebook
</div>
</div>
</div>
);
};
export default Login;
import React, { useContext, useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { auth } from "../firebase";
// import io from "socket.io-client";
// let socket = io.connect("wss://localhost:3000");
const AuthContext = React.createContext();
export const useAuth = () => useContext(AuthContext);
export const AuthProvider = ({ children }) => {
const [loading, setLoading] = useState(true);
const [user, setUser] = useState("");
const navigate = useNavigate();
useEffect(() => {
auth.onAuthStateChanged((user) => {
// socket.on();
setUser(user);
setLoading(false);
if (user) navigate("/chats");
});
}, [user, navigate]);
const value = { user };
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
);
};
Errors
closed socket
I am completely new to this and i do not know if the redirection problem also caused by socket or connection issues.If anybody has an idea about it i would be grateful.Thanks in advance!

in MERN, Response given and using useState to update new state with new fetched data, but not visually visible in my website even though logic works

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();

How to update Draft Value when click of Save Button?

I have a two Input form and a Paragraph, when I try to change the value of input the paragraph get updated, once the paragraph is updated I am trying to edit the paragraph with the help of drafts library, but once I update the paragraph and save it, it doesn't update the paragraph.
Please anyone Help me out to solve the problem
Codesandbox Link : Code
Context API
import React, { useState, createContext } from "react";
export const Contx = createContext();
export const ConProvider = ({ children }) => {
const [customerName, setCustomerName] = useState("");
const [amount, setAmount] = useState("");
const defaultValue = `<p>Hello ${
customerName === "" ? "User" : customerName
},</p>
<p>Please Click on the link below to pay the order - <strong>${amount}</strong> . </p>
<p>Click hear to pay</p>
<br/>
<p>Thanks and Regards</p>
<p>testUser</p>`;
const [draftValue, setDraftValue] = useState(defaultValue);
return (
<Contx.Provider
value={{
defaultValue,
setCustomerName,
setAmount,
customerName,
amount,
setDraftValue,
draftValue
}}
>
{children}
</Contx.Provider>
);
};
homePage
import React, { useContext, useState } from "react";
import ReactDOM from "react-dom";
import { ConProvider, Contx } from "../ContextApi";
import Data from "./Component/Data/Data";
import NewDraft from "./Component/Data/NewDraft";
import Modal from "./Component/Data/Modal";
import "./styles.css";
function App() {
const { defaultValue, setDraftValue, draftValue } = useContext(Contx);
// console.log("defaultValue", defaultValue);
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<div className="App">
<Data />
<Modal handleClose={handleClose} show={show}>
<NewDraft
prsFunc={setDraftValue}
handleClose={handleClose}
defaultValueEmpty={false}
defaultValue={defaultValue}
/>
</Modal>
<div
className="templateStyle p-2"
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{
__html: draftValue && draftValue
}}
/>
<button onClick={handleShow}>Edit</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<ConProvider>
<App />
</ConProvider>,
rootElement
);
Input Form
import React, { useContext } from "react";
import { Contx } from "../../../ContextApi";
export default function Data() {
const {
setCustomerName,
setDraftValue,
defaultValue,
setAmount,
customerName,
amount
} = useContext(Contx);
React.useEffect(() => {
setDraftValue(defaultValue);
});
// console.log("fffffff", customerName, amount);
return (
<div>
<input
type="text"
value={customerName}
name="customerName"
placeholder="Enter Customer name"
onChange={(e) => {
setCustomerName(e.target.value);
}}
/>
<input
type="number"
placeholder="Enter Amount"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
</div>
);
}
DraftJS
import React, { useState } from "react";
import { Editor } from "react-draft-wysiwyg";
import {
EditorState,
convertToRaw,
ContentState,
convertFromHTML
} from "draft-js";
import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css";
import draftToHtml from "draftjs-to-html";
import "./Data.css";
export default function NewDraft({
handleClose,
defaultValue,
defaultValueEmpty,
prsFunc
}) {
const initialState = defaultValueEmpty
? () => EditorState.createEmpty()
: EditorState.createWithContent(
ContentState.createFromBlockArray(convertFromHTML(defaultValue))
);
const [editorState, setEditorState] = useState(initialState);
const onChange = (value) => {
setEditorState(value);
};
const saveData = () => {
prsFunc(draftToHtml(convertToRaw(editorState.getCurrentContent())));
handleClose();
};
// console.log(draftToHtml(convertToRaw(editorState.getCurrentContent())));
return (
<div>
<div style={{ border: "2px solid", padding: "20px" }}>
<Editor
editorState={editorState}
toolbarClassName="toolbarClassName"
wrapperClassName="wrapperClassName"
editorClassName="editorClassName"
onEditorStateChange={(value) => onChange(value)}
/>
<button variant="secondary" onClick={saveData}>
Save
</button>
</div>
</div>
);
}
The problem you are facing is caused by this line in Data component. Every time the component is updated, the draftValue is set to the defaultValue.
React.useEffect(() => {
setDraftValue(defaultValue);
});

How to set properly react-router-dom links with React

I've been struggling with my first solo proyect due to my lack of experience, here is the trouble I have:
I need to set a Link component (of react-router-dom) which takes me to another new page when I clicked the button. Thought that selection I need to pass to the link (and the context) the id so I can fetch the data from the API, but I cannot get an idea of how to make it works. This is the button link component:
Model.jsx
import React, { useState, useContext } from 'react';
import { BrowserRouter as Router, Switch, Route, Link, Redirect } from 'react-router-dom';
import FileModel from "../FileModel/FileModel";
import { ModelsContext } from "../../context/ModelsContext";
const Model = ({modelo}) => {
const { id, name, year, price, photo } = modelo;
const { guardarModelo } = useContext(ModelsContext);
const [display, setDisplay] = useState("btn-notdisplayed");
const showButton = e => {
e.preventDefault();
setDisplay("btn-displayed");
};
const hideButton = e => {
e.preventDefault();
setDisplay("btn-notdisplayed");
};
return (
<div
className="card"
onMouseEnter={e => showButton(e)}
onMouseLeave={e => hideButton(e)}
>
<div className="card-body">
<p className="card-name">{name}</p>
<p className="card-yearprice">{year} | $ {price}</p>
</div>
<img src={`https://challenge.agenciaego.tech${photo}`} className="card-image" alt={`Imagen de ${name}`} />
<Router>
<button
type="button"
className={display}
onClick={() => {
guardarModelo(modelo);
}}
><Link to={`/models/${modelo.id}`}>Ver Modelo</Link>
</button>
<Switch>
<Route exact path={`/models/${modelo.id}`} component={FileModel} />
</Switch>
</Router>
</div>
);
}
export default Model;
Then I obtained the data from a context:
ModelsContext.jsx
import React, { createContext, useState, useEffect } from 'react';
export const ModelsContext = createContext();
const ModelsProvider = (props) => {
//State de modelos
const [ modelo, guardarModelo ] = useState({});
const [ modelos, guardarModelos ] = useState([]);
const [ allModelos, guardarAllModelo ] = useState([]);
//Cargar un modelo
useEffect(() => {
const consultarAPI = async () => {
const api = await fetch("https://challenge.agenciaego.tech/models");
const modelos = await api.json();
const api2 = await fetch(`https://challenge.agenciaego.tech/models/${id}`);
const modelo = await api2.json();
guardarAllModelo(modelos);
guardarModelos(modelos);
guardarModelo(modelo);
}
consultarAPI()
}, []);
return (
<ModelsContext.Provider
value={{
allModelos,
modelo,
modelos,
guardarModelo,
guardarModelos
}}
>
{props.children}
</ModelsContext.Provider>
)
}
export default ModelsProvider;
Finally, I got the App.js from which I route the principal component, the idea Is to get with the Link to the new component called "FileModel.jsx" as a child component an so maintains the Navbar component.
App.js
import React from "react";
import Navbar from "./components/Nav/Navbar";
import Models from "./components/Models/Models";
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';
import Logo from "./assets/img/logo.png";
import ModelsProvider from "./context/ModelsContext";
import ModelFooter from "./components/Models/ModelFooter";
function App() {
return (
<ModelsProvider>
<Router>
<nav className="navbar">
<img src={Logo} className="logo" alt="Ego Logo" />
<div className="menu-container">
<Link to={'/models'} className="menu-items">Modelos</Link>
<a className="menu-items">Ficha de modelo</a>
</div>
<div className="bottom-line"></div>
</nav>
<Switch>
<Route exact path='/models' component={Models} />
</Switch>
</Router>
<Navbar />
<ModelFooter />
</ModelsProvider>
);
}
export default App;
This is the fileModel.jsx so far:
import React, { useContext } from 'react';
import Navbar from "../Nav/Navbar";
import { ModelsContext } from "../../context/ModelsContext";
const FileModel = () => {
const { modelo } = useContext(ModelsContext);
console.log(modelo.id);
return (
<Navbar />
);
}
export default FileModel;
I hope to have explained my issue clear, and thanks so much to all you caring people!
Cheers!
Ps: Maybe you can find some things to refactor (I will need to check my code later), If you find something like that any help will be appreciated!
UPDATE
Due to Linda recommendation I merged the two contexts into one, and changed some lines of the code I wrote before, I cannot set the state to pass the solo model to the fileModel component and the Link still not working, I was thinking in a functions that can do it, I made another state, a single modelo, but when I click the button I got an error and undefined, because Idk how to set in the state the Id and so pass it to the API call, the terminal says that id in const api2 = await fetch(https://challenge.agenciaego.tech/models/${id}); is not defined.
I FINALLY SOLVED MY PROBLEM!!! I had to rellocate some elements between my component to make the api context take the id and pass it to the FileModel component, and change the Link router to the App.js, this is how I get to the solution:
App.js
import React from "react";
import Navbar from "./components/Nav/Navbar";
import Models from "./components/Models/Models";
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';
import Logo from "./assets/img/logo.png";
import ModelsProvider from "./context/ModelsContext";
import ModelFooter from "./components/Models/ModelFooter";
import FileModel from "./components/FileModel/FileModel";
function App() {
return (
<ModelsProvider>
<Router>
<nav className="navbar">
<img src={Logo} className="logo" alt="Ego Logo" />
<div className="menu-container">
<Link to={'/models'} className="menu-items">Modelos</Link>
<Link to={`/models/1`} className="menu-items">Ficha del Modelo</Link>
</div>
<div className="bottom-line"></div>
</nav>
<Switch>
<Route exact path='/models' component={Models} />
<Route exact path='/models' component={Models} />
</Switch>
</Router>
<Navbar />
<FileModel />
<ModelFooter />
</ModelsProvider>
);
}
export default App;
Modelscontext.jsx
import React, { createContext, useState, useEffect } from 'react';
export const ModelsContext = createContext();
const ModelsProvider = (props) => {
//State de modelos
const [ modelo, guardarModelo ] = useState([]);
const [ modelos, guardarModelos ] = useState([]);
const [ allModelos, guardarAllModelo ] = useState([]);
const { id } = modelo;
//Cargar un modelo
useEffect(() => {
const consultarAPI = async () => {
const api = await fetch("https://challenge.agenciaego.tech/models");
const modelos = await api.json();
const api2 = await fetch(`https://challenge.agenciaego.tech/models/${id}`);
const modelo = await api2.json();
guardarAllModelo(modelos);
guardarModelos(modelos);
guardarModelo(modelo);
}
consultarAPI()
}, [modelo.id]);
return (
<ModelsContext.Provider
value={{
allModelos,
modelo,
modelos,
guardarModelo,
guardarModelos
}}
>
{props.children}
</ModelsContext.Provider>
)
}
export default ModelsProvider;
Model.js
import React, { useState, useContext } from 'react';
import { Link } from 'react-router-dom';
import { ModelsContext } from "../../context/ModelsContext";
const Model = ({modelo}) => {
const { name, year, price, photo } = modelo;
const { guardarModelo } = useContext(ModelsContext);
const [display, setDisplay] = useState("btn-notdisplayed");
const showButton = e => {
e.preventDefault();
setDisplay("btn-displayed");
};
const hideButton = e => {
e.preventDefault();
setDisplay("btn-notdisplayed");
};
return (
<div
className="card"
onMouseEnter={e => showButton(e)}
onMouseLeave={e => hideButton(e)}
>
<div className="card-body">
<p className="card-name">{name}</p>
<p className="card-yearprice">{year} | $ {price}</p>
</div>
<img src={`https://challenge.agenciaego.tech${photo}`} className="card-image" alt={`Imagen de ${name}`} />
<Link to={`/models/${modelo.id}`}><button
type="button"
className={display}
onClick={() => {
guardarModelo(modelo);
}}
>Ver Modelo
</button></Link>
</div>
);
}
export default Model;
FileModel.js (so far...)
import React, { useContext } from 'react';
import Navbar from "../Nav/Navbar";
import { ModelsContext } from "../../context/ModelsContext";
const FileModel = () => {
const { modelo } = useContext(ModelsContext);
const { photo, name } = modelo;
return (
<>
<Navbar />
<section>
<img src={`https://challenge.agenciaego.tech${photo}`} alt={`Imagen de ${name}`}/>
</section>
</>
);
}
export default FileModel;
Thanks to Linda for giving me the input to refactoring the context! cheers!

Resources