So I was making a blog application where only logged in users can add new blogs. To start, when a user logs in, they will see all the blogs they have previously created on the frontend along with a form to add new ones. However, when the logged in user tries to add a new blog, it updates on the frontend but returns back to the original list they had before when the page is refreshed. I can see the updated blog list when I log out and log back in again. I actually used local storage to make sure that logged in users remain logged in after a new render. I just need help in making sure the new blogs added after login remain on the frontend after a render.
I think I have an idea why it is happening but I am not sure.
So whenever my loginService function is called within the handleLogin function, the server sends back the user info which includes all the blogs they have created. The problem with refreshing is due to the same list of blogs that were there at the time of login unless you log out and log in again.
Any help would be greatly appreciated.
ReactJS code
import { useState, useEffect } from 'react'
import Blog from './components/Blog'
import blogService from './services/blogs'
import loginService from './services/login'
import userService from './services/user'
const App = () => {
const [blogs, setBlogs] = useState([])
const [newBlogs, setNewBlogs] = useState([])
const [user, setUser] = useState(null)
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [errorMsg, setErrorMsg] = useState('')
const [blogTitle, setBlogTitle] = useState('')
const [blogAuthor, setBlogAuthor] = useState('')
const [blogUrl, setBlogUrl] = useState('')
useEffect( () => {
if(user != null){
setBlogs(user.blog.concat(newBlog))
}
console.log("blogs is", blogs)
}, [user])
//Seeing if a user is logged in on rerender
useEffect(() => {
const loggedInUser = window.localStorage.getItem('loggedBlogUser')
if(loggedInUser){
const user = JSON.parse(loggedInUser)
setUser(user)
}
},[])
// Logging in users
const handleLogin = async (event) => {
event.preventDefault()
console.log("Logging in,", username, password)
try {
const user = await loginService({username, password})
blogService.setToken(user.token)
window.localStorage.setItem('loggedBlogUser', JSON.stringify(user))
setUser(user)
setUsername('')
setPassword('')
}
catch(error){
setErrorMsg('Wrong credentials')
setTimeout(() => {
setErrorMsg(null)
},[])
}
}
//Logging out users
const handleLogout = () => {
window.localStorage.removeItem('loggedBlogUser')
setUser(null)
setBlogs([])
}
//Adding new blogs
const addNewBlog = async (e) => {
e.preventDefault()
console.log("User here is", user)
try {
const newBlog = {
title: blogTitle,
author: blogAuthor,
url: blogUrl
}
await blogService.createBlog(newBlog)
setBlogs(blogs.concat(newBlog))
setNewBlogs(newBlogs.concat(newBlog))
setBlogTitle('')
setBlogAuthor('')
setBlogUrl('')
}
catch(error){
console.log("error adding new blog", error)
}
console.log("blogs is", blogs)
}
return (
<div>
<h2>blogs</h2>
{user == null && <div className="login-form">
<form onSubmit={handleLogin}>
<div className="username-container">
username
<input type='text' value={username} onChange={(e) => setUsername(e.target.value)} name='username'/>
</div>
<div className="password-container">
password
<input type='password' value={password} onChange={(e) => setPassword(e.target.value)} name='password'/>
</div>
<button type='submit'>Login</button>
</form>
</div>}
{user != null && <div className="notes">
<p>{user.name} logged in <button onClick={handleLogout}>logout</button></p>
</div>}
{user != null && <div className="addBlog-container">
<b>create new</b>
<form onSubmit={addNewBlog}>
<label>Title:</label><input type="text" value={blogTitle} onChange={(e) => setBlogTitle(e.target.value)} name="blog-title"/>
<label>Author:</label><input type="text" value={blogAuthor} onChange={(e) => setBlogAuthor(e.target.value)} name="blog-author"/>
<label>Url:</label><input type="text" value={blogUrl} onChange={(e) => setBlogUrl(e.target.value)} name="blog-url"/>
<button type='submit'>create blog</button>
</form>
</div>}
{blogs != null && blogs.map(blog =>
<Blog key={blog.id} blog={blog} />
)}
</div>
)
}
export default App
At first you are updating blogs from user.blog but in update blog you are only updating blogs variable, that's why new blog disappears as soon as you refresh. Try after updating user.blog with new blog.
Related
I'm currently converting the logic in my mern (with typescript) project to use React/Tanstack query to learn this tool better.
I want to use useMutation to handle the post request logic from the details inputted in the form, in this login component but can't figure out how to do this. Any tips would be appreciated thanks. Below is the code from my login component
const Login = () => {
const navigate = useNavigate();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [errorMsg, setErrorMsg] = useState("");
const [state, setState] = useContext(UserContext);
const handleSubmit = async (e: { preventDefault: () => void }) => {
e.preventDefault();
let response;
const { data: loginData } = await axios.post("http://localhost:5001/auth/login", {
email,
password,
});
response = loginData;
if (response.errors.length) {
return setErrorMsg(response.errors[0].msg);
}
setState({
data: {
id: response.data.user.id,
email: response.data.user.email,
stripeCustomerId: response.data.user.stripeCustomerId,
},
loading: false,
error: null,
});
localStorage.setItem("token", response.data.token);
axios.defaults.headers.common["authorization"] = `Bearer ${response.data.token}`;
navigate("/dashboard");
};
return (
<div className="login-card">
<div>
<h3>Login</h3>
</div>
<form onSubmit={handleSubmit}>
<div className="login-card-mb">
<label>Email</label>
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
</div>
<div className="login-card-mb">
<label>Password</label>
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
</div>
{errorMsg && <p>{errorMsg}</p>}
<button type="submit">Submit</button>
</form>
</div>
);
};
After setting up your project to use React Query ( Check the docs if you have not). You want to extract your api call to a separate function that takes an object. This object will hold the values you would like to post.
const Login = (dataToPost) => {
let res = await axios.post('url', dataToPost)
return res.data
}
Now that you have that, you can import useMutation from React Query. Once imported you can now use the hook. UseQuery, useMutation both contain a data variable so no need to create state for the data returned from your endpoint. In this example, I'm deconstructing the data and loading state. But most importantly the mutate function. Which allows you to fire off your api call. We add our api call to the hook. I'm renaming the mutate function to doLogin. It's a habit
const {data,isLoading,mutate:doLogin} = useMutation(Login)
Finally we can just call mutate(objectWithValues) wherever you want in your code. The data will initially be null and isLoading will be true once called. To tie it all together. Your handleSubmit could look as follows
const handleSubmit = () => {
e.preventDefault();
doLogin({email,password})
}
You also have the option of running functions on a success or error of the mutation
const {data,isLoading,mutate: doLogin} =
useMutation(Login, {
onError: (err) => console.log("The error",err),
onSuccess:(someStuff)=>console.log("The data being returned",someStuff)
})
I'm using Firebase Web version 9 in a react app. I have a sign-up form with first name, email, and password inputs. When the form is submitted, I need to create an authorized user with Firebase and immediately update that new user's first name and profile picture.
I'm using two Firebase auth functions - createUserWithEmailAndPassword() and updateProfile(). A new user is always created when the form is submitted, but the profile updates only happen once in a while. I haven't been able to pinpoint the cases when the profile update is successful.
Any ideas on what I'm missing? Would love some feedback and guidance. Thank you!
Here's the code for the sign-up page.
import { useState, useEffect } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import {
StyledHeader,
StyledFooter,
StyledDropdown,
StyledForm,
} from '../styles';
import {
getAuth,
createUserWithEmailAndPassword,
updateProfile,
} from 'firebase/auth';
import { ShortFooter, LanguageSelect, Form } from '../components';
import { logo, p1, p2, p3, p4, p5 } from '../assets';
const SignUp = ({ children }) => {
const [firstName, setfirstName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [isFirstNameEmpty, setIsFirstNameEmpty] = useState(true);
const [isEmailEmpty, setIsEmailEmpty] = useState(true);
const [isPasswordEmpty, setIsPasswordEmpty] = useState(true);
const [error, setError] = useState('');
const [checked, setChecked] = useState(true);
const [isShown, setIsShown] = useState(true);
const isInvalid = firstName === '' || email === '' || password === '';
const navigate = useNavigate();
const userProfileImgs = [p1, p2, p3, p4, p5];
useEffect(() => {
setTimeout(() => {
// 👇️ scroll to top on page load
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
}, 100);
}, []);
const handlefirstNameChange = (firstName) => {
if (firstName.length !== 0) {
setIsFirstNameEmpty(false);
} else {
setIsFirstNameEmpty(true);
}
};
const handleEmailChange = (email) => {
if (email.length !== 0) {
setIsEmailEmpty(false);
} else {
setIsEmailEmpty(true);
}
};
const handlePasswordChange = (password) => {
if (password.length !== 0) {
setIsPasswordEmpty(false);
} else {
setIsPasswordEmpty(true);
}
};
const handleCheckbox = () => {
setChecked((checked) => !checked);
};
const handleLearnMore = (e) => {
e.preventDefault();
setIsShown((isShown) => !isShown);
};
const handleSignUp = async (e) => {
e.preventDefault();
try {
// firebase work!
const auth = getAuth();
let { user } = await createUserWithEmailAndPassword(
auth,
email,
password
);
console.debug(`User ${user.uid} created`);
// updating the user's profile with their first name and a random profile image
await updateProfile(user, {
displayName: firstName,
photoURL:
userProfileImgs[Math.floor(Math.random() * userProfileImgs.length)],
});
console.debug(`User profile updated`);
// navigate to the profile page
navigate('/profile');
} catch (e) {
if (e.message === 'Firebase: Error (auth/email-already-in-use).') {
setError('That email is already in use, please try again.');
}
}
};
return (
<>
<StyledHeader height="120">
<div className="header__background">
<div className="header__frame">
<div>
<Link to="/">
<img className="header__logo" src={logo} alt="Netflix Logo" />
</Link>
</div>
</div>
<form onSubmit={handleSignUp} className="form__container">
<StyledForm padding="20px 68px 40px">
<Form
error={error}
isEmailEmpty={isEmailEmpty}
email={email}
setEmail={setEmail}
handleEmailChange={handleEmailChange}
isPasswordEmpty={isPasswordEmpty}
password={password}
setPassword={setPassword}
handlePasswordChange={handlePasswordChange}
isInvalid={isInvalid}
checked={checked}
handleCheckbox={handleCheckbox}
handleLearnMore={handleLearnMore}
isShown={isShown}
isFirstNameEmpty={isFirstNameEmpty}
firstName={firstName}
setfirstName={setfirstName}
handlefirstNameChange={handlefirstNameChange}
method="post"
/>
</StyledForm>
</form>
<div className="signIn__footer-container">
<div className="signIn__footer-background">
<StyledFooter
backgroundColor="transparent"
padding="10px 70px 40px 70px"
>
<ShortFooter>
<StyledDropdown>
<LanguageSelect />
</StyledDropdown>
</ShortFooter>
</StyledFooter>
</div>
</div>
</div>
{children}
</StyledHeader>
</>
);
};
export default SignUp;
This is the error I'm getting in the console.
Error
Based on how my app was set up I ultimately decided to remove the photoURL property altogether since it could be defined elsewhere in the app. After testing, only one of my images worked. This is why I experienced the full profile updates working out sometimes since random images were selected each time a new user signs up. I ended up sticking wth this image as the default image.
As #adsy mentioned, my profile updates weren't happening consistently because certain profile images I had exceeded the allowed input body length for this property. The example in the Docs shows a link with 43 characters but no other information. The path to the profile images in the question are much shorter than this so I still need some understanding.
After researching a bit, I didn't come across the maximum limit set. It's now something to contact Google Developer's support team about. If anyone has the information for the allowed input body length for this property, feel free to share it on this post.
Thanks!
const handleSignUp = async (e) => {
e.preventDefault();
try {
// firebase work!
const auth = getAuth();
let { user } = await createUserWithEmailAndPassword(
auth,
email,
password
);
console.debug(`User ${user.uid} created`);
// updating the user's profile with their first name
await updateProfile(user, {
displayName: firstName,
});
console.debug(`User profile updated`);
// navigate to the profile page
navigate('/profile');
} catch (e) {
if (e.message === 'Firebase: Error (auth/email-already-in-use).') {
setError('That email is already in use, please try again.');
}
}
};
I am currently following a tutorial to make a simple frontend application that uses Firebase for authentication purposes. My code seemed to work when I only had the logic for registering a new user, however, something went amiss when I added the logic for a user trying to login. Every time I simply click into the Email box for logging in, my app freezes.
Here is the display of my application (ignore blue line):
Here is the code for App.js:
import { useState } from 'react';
import './App.css';
import { createUserWithEmailAndPassword,
onAuthStateChanged,
signInWithEmailAndPassword,
signOut } from 'firebase/auth';
import { auth } from './firebase-config'
function App() {
const [registerEmail, setRegisterEmail] = useState('')
const [registerPassword, setRegisterPassword] = useState('')
const [user, setUser] = useState({});
const [loginEmail, setLoginEmail] = useState('')
const [loginPassword, setLoginPassword] = useState('')
onAuthStateChanged(auth, (currentUser) => {
setUser(currentUser)
}, [])
const register = async() => {
try {
const user = await createUserWithEmailAndPassword(auth, registerEmail, registerPassword);
console.log(user)
} catch (err) {
console.log(err.message)
}
};
const login = async() => {
try {
const user = await signInWithEmailAndPassword(auth, loginEmail, loginPassword);
console.log(user)
} catch (err) {
console.log(err.message)
}
};
const logout = async() => {
await signOut(auth);
};
return (
<div className="App">
<div>
<h3> Register User </h3>
<input placeholder='Email...' onChange={(e) => setRegisterEmail(e.target.value)}/>
<input placeholder='Password...' onChange={(e) => setRegisterPassword(e.target.value)}/>
<button onClick={register}> Create User</button>
</div>
<div>
<h3> Login </h3>
<input placeholder='Email...' onChange={(e) => setLoginEmail(e.target.value)}/>
<input placeholder='Password...' onChange={(e) => setLoginPassword(e.target.value)}/>
<button onClick={login}> Login</button>
</div>
<h4> User Logged In: </h4>
{user?.email}
<button onClick={logout}> Sign Out </button>
</div>
);
}
export default App;
Finally, here is the code for the firebase.config.js file:
import { initializeApp } from "firebase/app";
import { getAuth } from 'firebase/auth';
const firebaseConfig = {
apiKey: "AIzaSyANfrqNmIbJLZlN-j6oOrjL8ZRv-YYM10M",
authDomain: "authentication-tutorial-3259a.firebaseapp.com",
projectId: "authentication-tutorial-3259a",
storageBucket: "authentication-tutorial-3259a.appspot.com",
messagingSenderId: "145171676516",
appId: "1:145171676516:web:044dfc2fc86abbb1d74e71"
};
const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
You need to add values to both your inputs. The value is not changing, hence you get the feeling of you app getting stuck.
For signup inputs
<input value={registerEmail} placeholder='Email...' onChange={(e) => setRegisterEmail(e.target.value)}/>
<input value={registerPassword} placeholder='Password...' onChange={(e) => setRegisterPassword(e.target.value)}/>
For login inputs
<input value={loginEmail} placeholder='Email...' onChange={(e) => setLoginEmail(e.target.value)}/>
<input value={loginPassword} placeholder='Password...' onChange={(e) => setLoginPassword(e.target.value)}/>
You need to put the onAuthStateChanged inside the useEffect hook, and don't forget to add the emty [] afterwards.
Putting onAuthStateChanged into useEfffect this way solved the problem for me:
useEffect(() => {
onAuthStateChanged(auth, (currentUser) => {
setUser(currentUser);
});
}, []);
I am learning React with Fullstack with the University of Helsinki. In one of the exercises, I need to build a frontend app and connect it with a previously built backend server.
I need to implement a login/logout function.
The issue is in my HandleLogin section. For some reason, if I use promises for the axios HTTP request directly in my frontend app.js. It has no problem connecting the server. However, if I create a new component for the login function to handle the login button. I cannot receive anything from the server. I tried console.log(user) in the HandleLogin, it shows the user with axio/http but not with async/await.
Could anyone explain to me why this is happening? Thank you very much!
My codes are here.
App.js
import blogService from './services/blogs'
import loginService from './services/login'
import Blog from './components/Blog'
import LoginForm from './components/LoginForm'
import BlogForm from './components/BlogForm'
import Togglable from './components/Togglable'
import Notification from './components/Notification'
import axios from 'axios'
const App = () => {
const [blogs, setBlogs] = useState([])
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [user, setUser] = useState(null)
const [loginVisible, setLoginVisible] = useState(false)
const [Notification, setNotification] = useState("")
const [Toggle, setToggle] = useState(false)
useEffect(() => {
const Data = async () => {
const initialBlogs = await blogService.getAll()
setBlogs( initialBlogs )
}
Data()
}, [])
useEffect(() => {
const loggedUserJSON = window.localStorage.getItem('loggedBlogAppUser')
if (loggedUserJSON) {
const user = JSON.parse(loggedUserJSON)
setUser(user)
blogService.setToken(user.token)
}
}, [])
const addBlog = async (blogObject) => {
BlogFormRef.current.toggleVisibility()
if (blogObject.title !== '' && blogObject.author !== '' && blogObject.url !== '') {
const newBlog = await blogService.create(blogObject)
setBlogs(blogs.concat(newBlog))
setNotification(`A new blog ${blogObject.title} by ${blogObject.author} is added`)
setToggle(!Toggle)
setTimeout(() => {
setToggle(false)
}, 5000)
} else {
setNotification('You must fill all fields to create a blog')
setToggle(!Toggle)
setTimeout(() => {
setToggle(false)
}, 5000)
}
}
const BlogFormRef = useRef()
const blogUpdate = async (blogId, blogObject) => {
await blogService.update(blogId, blogObject)
const updatedBlog = {...blogObject, blogId}
setBlogs(blogs.map((blog) => (blog.id === updatedBlog.id ? updatedBlog : blog)))
}
const blogRemove = async (blogId) => {
await blogService.remove(blogId)
setBlogs(blogs.filter((blog) => blog.id !== blogId))
}
const handleLogin = async (event) => {
event.preventDefault()
try{
const user = await axios.post(`http://localhost:3001/api/login`, {username, password})
/*const user = await loginService.login({
username,
password
})*/ //this is not working
console.log(user)
console.log(user.data.name)
window.localStorage.setItem('loggedBlogAppUser', JSON.stringify(user))
blogService.setToken(user.data.token)
setUser(user)
setUsername('')
setPassword('')
setNotification(`User ${user.data.name} is logged in`)
} catch (exception) {
setNotification('Wrong username or password')
setToggle(!Toggle)
setTimeout(() => {
setToggle(false)
}, 5000)
}
}
//console.log(user)
const handleLogout = () => {
window.localStorage.removeItem('loggedBlogAppUser')
document.location.reload()
}
const blogForm = () => (
<Togglable buttonLabel='new blog' cancelButtonLabel="Cancel" ref={BlogFormRef}>
<BlogForm createBlog={addBlog} />
</Togglable>
)
const loginForm = () => {
const hideWhenVisible = { display: loginVisible ? 'none' : ''}
const showWhenVisible = { display: loginVisible ? '' : 'none'}
return(
<div>
<div style={hideWhenVisible}>
<button onClick={() => setLoginVisible(true)}>log in</button>
</div>
<div style={showWhenVisible}>
<LoginForm
username={username}
password={password}
handleUsernameChange={({target}) => setUsername(target.value)}
handlePasswordChange={({target}) => setPassword(target.value)}
handleSubmit={handleLogin}/>
</div>
</div>
)
}
return (
<div>
<h2>Blog11</h2>
{user && (
<div>
{user.data.name} is logged in
<button onClick={handleLogout}>Logout</button>
</div>
)}
<div>
{user === null ? loginForm()
:(
<>
{blogForm()}
<div>
{blogs.map(blog =>
<Blog
key={blog.id}
blog={blog}
blogUpdate={blogUpdate}
blogRemove={blogRemove}/>
)}
</div>
</>
)
}
</div>
</div>
)
}
export default App
loginService
const baseUrl = `/api/login`
const login = async (credentials) => {
const response = await axios.post(baseUrl, credentials)
return response.data
}
export default login
This is the first time for me to ask a question on Stackoverflow. Thank you all for your patience.
I gave up using an independent module. Instead, I used await axios.post in the App.js and there is no problem.
try{
const user = await axios.post(`http://localhost:3001/api/login`, {username, password})
/*const user = await loginService.login({
username,
password
})*/ //this is not working
//console.log(user)
//console.log(user.data.name)
window.localStorage.setItem('loggedBlogAppUser', JSON.stringify(user))
blogService.setToken(user.data.token)
setUser(user)
setUsername('')
setPassword('')
setNotification(`User ${user.data.name} is logged in`)
} catch (exception) {
setNotification('Wrong username or password')
setToggle(!Toggle)
setTimeout(() => {
setToggle(false)
}, 5000)
}
}
I am building a chat app and trying to match the id params to render each one on click.I have a RoomList component that maps over the rooms via an endpoint /rooms
I then have them linked to their corresponding ID. THe main components are Chatroom.js and RoomList is just the nav
import moment from 'moment';
import './App.scss';
import UserInfo from './components/UserInfo';
import RoomList from './components/RoomList';
import Chatroom from './components/Chatroom';
import SendMessage from './components/SendMessage';
import { Column, Row } from "simple-flexbox";
import { Route, Link, Switch } from 'react-router-dom'
function App() {
const timestamp = Date.now();
const timeFormatted = moment(timestamp).format('hh:mm');
const [username, setUsername] = useState('');
const [loggedin, setLoggedin] = useState(false);
const [rooms, setRooms] = useState([]);
const [roomId, setRoomId] = useState(0);
const handleSubmit = async e => {
e.preventDefault();
setUsername(username)
setLoggedin(true)
};
useEffect(() => {
let apiUrl= `http://localhost:8080/api/rooms/`;
const makeApiCall = async() => {
const res = await fetch(apiUrl);
const data = await res.json();
setRooms(data);
};
makeApiCall();
}, [])
const handleSend = (message) => {
const formattedMessage = { name: username, message, isMine: true};
}
return (
<div className="App">
<Route
path="/"
render={(routerProps) => (
(loggedin !== false) ?
<Row>
<Column>
{/*<Chatroom roomId={roomId} messages={messages} isMine={isMine}/>*/}
</Column>
</Row>
:
<form onSubmit={handleSubmit}>
<label htmlFor="username">Username: </label>
<input
type="text"
value={username}
placeholder="enter a username"
onChange={({ target }) => setUsername(target.value)}
/>
<button type="submit">Login</button>
</form>
)}
/>
<Switch>
<Route
exact
path="/:id"
render={(routerProps) => (
<Row>
<Column>
<UserInfo username={username} time={timeFormatted}/>
<RoomList rooms={rooms}/>
</Column>
<Column>
<Chatroom {...routerProps} roomId={roomId}/>
<SendMessage onSend={handleSend}/>
</Column>
</Row>
)}
/>
</Switch>
</div>
);
}
export default App;
RoomList.js
import { Row } from "simple-flexbox";
const RoomList = (props) => {
return (
<div className="RoomList">
<Row wrap="false">
{
props.rooms.map((room, index) => {
return (
<Link to={`/${room.id}`} key={index}>{room.id} {room.name}</Link>
)
})
}
</Row>
</div>
)
}
export default RoomList;
Chatroom.js
this is the main component that should render based on the ID
import Message from './Message';
import { Link } from 'react-router-dom'
const Chatroom = (props) => {
const [roomId, setRoomId] = useState(0);
const [name, setName] = useState('Roomname')
const [messages, setMessages] = useState([]);
useEffect(() => {
let apiUrl= `http://localhost:8080/api/rooms/`;
const id = props.match.params.id;
const url = `${apiUrl}${id}`;
const makeApiCall = async () => {
const res = await fetch(url);
const data = await res.json();
setRoomId(data.id);
setUsers(data.users)
setName(data.name)
};
makeApiCall();
}, []);
useEffect(() => {
const id = props.match.params.id;
const url = `http://localhost:8080/api/rooms/${id}/messages`;
const makeApiCall = async() => {
const res = await fetch(url);
const data = await res.json();
setMessages(data);
};
makeApiCall();
}, [])
return (
<div className="Chatroom">
{name}
</div>
)
}
export default Chatroom;```
when I click on the links I want the change to refresh the new content but it wont? any ideas why ? thank you in advance!
Notice that your functional component named App does not have any dependencies and that is fine since data should just be fetched once, on mount. However, on ChatRoom we want a new fetch everytime that roomId changes.
First thing we could do here is adding props.match.params.id directly into our initial state.
const [roomId, setRoomId] = useState(props.match.params.id); // set up your initial room id here.
Next we can add an effect that checks if roomId needs updating whenever props change. Like this:
useEffect(()=>{
if(roomId !== props.match.params.id) {
setRoomId(props.match.params.id)
}
}, [props])
Now we use roomId as our state for the api calls and add it in the brackets (making react aware that whenever roomId changes, it should run our effect again).
useEffect(() => {
let url = "http://localhost:8080/api/rooms/" + roomId; // add room id here
const makeApiCall = async () => {
const res = await fetch(url);
const data = await res.json();
setUsers(data.users)
setName(data.name)
};
makeApiCall();
}, [roomId]); // very important to add room id to your dependencies as well here.
useEffect(() => {
const url = `http://localhost:8080/api/rooms/${roomId}/messages`; // add room id here as well
const makeApiCall = async() => {
const res = await fetch(url);
const data = await res.json();
setMessages(data);
};
makeApiCall();
}, [roomId]) // very important to add room id to your dependencies as well here.
I believe that it should work. But let me build my answer upon this:
When mounted, meaning that this is the first time that the ChatRoom is rendered, it will go through your useEffect and fetch data using roomId as the initial state that we setup as props.match.params.id.
Without dependencies, he is done and would never fetch again. It would do it once and that's it. However, by adding the dependency, we advise react that it would watch out for roomId changes and if they do, it should trigger the function again. It is VERY IMPORTANT that every variable inside your useEffect is added to your brackets. There is eslint for it and it is very useful. Have a look at this post. It helped me a lot.
https://overreacted.io/a-complete-guide-to-useeffect/
Let me know if it works and ask me if there is still doubts. =)