How can I decently transfer props between my components? - reactjs

I want to provide a transfer of props between my two components dec.
So when I want to go to my user component from my home page, I want to show the user's name.
My app.js component:
<Link
to={{
pathname: "/user",
query: {
name: user.name
}
}}
target="_blank"
>
Link
</Link>
export const getServerSideProps = async (context) => {
console.log(context.query,"asdf");
return {
props: {
name: context.query.name
}
};
};
I am trying to send the props in query to my user component using the getServerSideProps method.
my user jsx component :
import React from "react";
const User = (props) => {
const {name} = props;
return (
<>
<span>{props.name} </span>
</>
);
};
export default User;
My main and biggest problem here is not being unable to return. Because I can't see the object with the console log. How can I solve this problem?

Related

Button don't passing data to another component. React

I have a component which is a button. Then in another component i am looping trough concerts and using this button to redirect to booking page but after clicking my data is not passed.
This is my button component:
import React from "react";
export const BookBtn = (props) => {
return (
<div>
<button
className="bookBtn"
style={{ backgroundColor: props.color }}
// onClick={props.func}
>
{props.text}
</button>
</div>
);
};
BookBtn.defaultProps = {
text: "Unavailable",
};
export default BookBtn;
Here is the button in my main component where I try to click
<a href={"/concert/" + concert.id} data={concert}>
<BookBtn text="Book a ticket" />
</a>
Here is my component where i try to redirect to and retrive my data.
import React from "react";
import { useEffect, useState } from "react";
import axios from "axios";
export const Book = (data) => {
const [concerts, setConcerts] = useState([]);
const [tickets, setTickets] = useState([]);
useEffect(() => {
const loadConcerts = async () => {
const resConcerts = await axios.get("data/concerts");
const tickets = await axios.get("/data/tickets");
};
});
return (
<div>
Booking page
<h1>{data.name}</h1>
</div>
);
};
UPDATE:
I wrapped my button in anchor tag and now i am able to redirect but still can't pass data.
Final Update
Allright, i managed to pass my data using useLocation hook.
Problem is solved.
I'd suggest using react-router to do the redirection or routing instead of anchor tags as they cause a refresh.
Use the Link tag from react-router and pass the concert state along with it!
Have a look at this https://reactrouter.com/en/main/components/link.

Next.js > How to Implement Dynamic Route Based Modals

Is it possible to implement a dynamic route based modal in Next.js 13 (using the app directory) ?
For reference, I'm aiming to create a similar flow to how the homepage of nomadlist (and instagram) is, such that when you click on a card, a modal appears with the updated route, i.e. https://nomadlist.com/madeira, and when visiting the link link directly, you are taken to the full page.
I am also adding the list items that have modal's to my app's root directory, and I would like to have the modal route URL nested like: /users/[userId]
What I need to do is this:
When triggering the modal in my app's list view, update the current URL without refreshing the browser. (i.e. using a State / context to trigger the modal). i.e. /users/[userId]
When visiting the modal's route directly, i.e. /users/123-43249, to display a full page of the user.
I've tried a bunch of different things, but can't seem to get this to work, so I'm more curious if this is even possible before spending more time on it.
The current structure of my app is this:
// layout.tsx
export default async function RootLayout({ children }) {
return(
<html>
<head />
<body>
<ProfileModalProvider>
{children}
</ProfileModalProvider>
</body>
</html>
)
}
// page.tsx
export default async function Home() {
const users = await getUsers();
return (
<main>
<Hero />
<SearchSection />
<UserList users={users} />
<ProfileModal /> // Should I import the whole page from '/users/[userId] here?
</main>
);
}
// ViewUserModalButton.tsx (comes from the UserList.tsx)
export default function ViewProfileButton({ user }: { user: IUser }) {
return (
<Link
as={`/users/${user.id}`}
href={{
pathname: "/users/[userId]",
query: { userId: user.id },
}}
scroll={false}
shallow={true}
>
<div>
View User
</div>
</Link>
);
}
Thank you so much.
The only way I was able to implement this was to override the default behavior of the open modal function and append URL to the window's history.
const [{ shouldShowModal }, setShouldShowModal] = useProfileModal();
const toggleModal = e => {
e.preventDefault();
setShouldShowModal({ shouldShowModal: true, profile });
window.history.pushState({}, "", `/users/${profile.id}`);
}
export default function ViewProfileButton({ user }: { user: IUser }) {
return (
<Link
as={`/users/${user.id}`}
href={{
pathname: "/users/[userId]",
query: { userId: user.id },
}}
scroll={false}
shallow={true}
onClick={toggleModal}
>
<div>
View User
</div>
</Link>
);
}

How to add a logout to my React navbar with JWT?

I am quite new to react and JavaScript. I am trying to make a dynamic navigation bar that shows certain links if the user is logged in or logged out. I want to base it off of if the JWT token is present or not. I am stuck on how to implement 'checking if a user is logged in using tokens' into my Navbar function, so the boolean works to use one component if public or one component if logged in.
import "./navbar.css"
import NavLoggedIn from "./navLoggedIn"
import NavPublic from "./navPublic"
const Navbar = () => {
const token = window.localStorage.getItem("token");
return (
<>
{ token === null ? <NavPublic /> : <NavLoggedIn /> }
</>
);
};
export default Navbar;
import "./navbar.css"
const NavLoggedIn = () => {
return (
<>
<nav className="nav">
Acebook
<li>
profile
</li>
<li>
posts
</li>
<li>
logout
</li>
</nav>
</>
);
}
export default NavLoggedIn ;
import "./navbar.css"
const NavPublic = () => {
return (
<>
<nav className="nav">
Acebook
<ul>
<li>
signup
</li>
<li>
login
</li>
</ul>
</nav>
</>
);
}
export default NavPublic;
So the problem with the current approach is that the NavBar component only checks the token in localStorage when it mounts. It's "unaware" of subsequent changes to the authentication status.
I propose an alternative solution in which we use the Context API. We can start by introducing a new component:
import React, { createContext, useState } from 'react'
const AuthenticationContext = createContext({})
const AuthenticationProvider = ({ children }) => {
const [isLoggedIn, setIsLoggedIn] = useState(false)
return (
<AuthenticationContext.Provider value={{isLoggedIn, setIsLoggedIn}}>
{children}
</AuthenticationContext.Provider>
)
}
export default AuthenticationProvider
This component has a named export, which exports a context and a default export which exports the provider. We can wrap the entire app in the provider. Assuming the root component is called <App/> , we can do this:
<AuthenticationProvider>
<App/>
</AuthenticationProvider>
Now, in any component in your app that you can access this context like so:
import React, { useContext } from 'react'
import { AuthenticationContext } from 'path/to/AuthenticationProvider'
const { isLoggedIn, setIsLoggedIn} = useContext(AuthenticationContext)
In your login function you would call setIsLoggedIn(true) and in your logout function you would call setIsLoggedIn(false). Within your NavBar component you would check the value of isLoggedIn. The NavBar component should "see" whenever the value changes and render the correct Nav component.

How to make a "go back" button change state in home screen

So ive got a list of restaurant names (say fetched from an api). When I click on a restaurant name, I want it to link to a profile page for that specific restaurant, and this would set the text as "selected". And when I click "Go back" on that profile page to return to the home page, I want the that restaurant name to say "not selected".
So, if I click on the restaurant name, then in the profile page go back to the home page, the restaurant will show "unselected" since it was selected in the home page, then unselected in the profile page. However, if I click on the restaurant name, then instead of going back to the home page by clicking the "go back", I type in the url of the home page, it will show "selected".
I'm struggling with making it so when I click "Go back", the home page shows the restaurant name as having "unselected".
https://codesandbox.io/s/serene-williams-2snv1c?file=/src/App.js
(I would also appreciate if I could get the name of this sort of concept so I can look it up myself)
If I'm understanding the question correctly, you want to set some "selected" state, and only clear it if the link from the detail page is clicked.
You can create a React Context to hold and provide out the clickedRestaurants state and updater functions.
The idea here is to use the selectRestaurant handler when navigating "forward" to the details page, and use the deselectRestaurant handler only when the link from the details page back to the home page is clicked. If a user navigates to the home page using any other method, the restaurant won't be de-selected.
The localStorage API is used to persist state changes and initialize the state. The resolves persisting the selected restaurants state when the page is reloaded or a user directly mutates the URL in the address bar, i.e. like manually navigating back to "/".
RestaurantProvider
import { createContext, useContext, useEffect, useState } from "react";
export const RestaurantContext = createContext();
export const useRestaurantContext = () => useContext(RestaurantContext);
const RestaurantProvider = ({ children }) => {
const [clickedRestaurants, setClickedRestaurants] = useState(() => {
return JSON.parse(localStorage.getItem("clickedRestaurants")) ?? {};
});
useEffect(() => {
localStorage.setItem(
"clickedRestaurants",
JSON.stringify(clickedRestaurants)
);
}, [clickedRestaurants]);
const setRestaurantState = (id, selected) => {
setClickedRestaurants((ids) => ({
...ids,
[id]: selected
}));
};
const selectRestaurant = (id) => setRestaurantState(id, true);
const deselectRestaurant = (id) => setRestaurantState(id, false);
return (
<RestaurantContext.Provider
value={{ clickedRestaurants, selectRestaurant, deselectRestaurant }}
>
{children}
</RestaurantContext.Provider>
);
};
export default RestaurantProvider;
index.js - Import and wrap the application components with the RestaurantProvider component created above.
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import RestaurantProvider from "./RestaurantProvider";
import App from "./App";
import Details from "./details";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(
<StrictMode>
<RestaurantProvider>
<App />
<Details />
</RestaurantProvider>
</StrictMode>
);
App - Import and use the useRestaurantContext hook to access the state and updater functions.
import "./styles.css";
import { Link, Route } from "wouter";
import data from "./data";
import { useRestaurantContext } from "./RestaurantProvider";
export default function App() {
const { clickedRestaurants, selectRestaurant } = useRestaurantContext();
return (
<Route path="/">
<div className="App">
{data.map((restaurant) => {
return (
<Button
key={restaurant}
restaurant={restaurant}
hasBeenClicked={clickedRestaurants[restaurant]}
setClicked={() => selectRestaurant(restaurant)}
/>
);
})}
</div>
</Route>
);
}
function Button({ restaurant, hasBeenClicked, setClicked }) {
return (
<>
<Link href={`/restaurant/${restaurant}`} onClick={setClicked}>
<button>{restaurant}</button>
</Link>
<p>
{restaurant} has {hasBeenClicked ? "" : "not "}been selected
</p>
</>
);
}
Details
import "./styles.css";
import { Link, Route } from "wouter";
import { useRestaurantContext } from "./RestaurantProvider";
export default function Details() {
const { deselectRestaurant } = useRestaurantContext();
return (
<div className="App">
<Route path="/restaurant/:name">
{(params) => {
const restaurant = decodeURI(params.name);
return (
<Link href="/" onClick={() => deselectRestaurant(restaurant)}>
{restaurant} Go back and unselect
</Link>
);
}}
</Route>
</div>
);
}
You'll need to define some sort of state if you want to be able to tell what has been clicked by the user and what hasn't. Here is one way to do it:
App.js
import "./styles.css";
import data from "./data";
import { Link, Route } from "wouter";
import { useState } from "react";
export default function App() {
const [clickedRestaurants, setClickedRestaurants] = useState([])
return (
<Route path="/">
<div className="App">
{data.map((restaurant) => {
return (
<Button
restaurant={restaurant}
hasBeenClicked={clickedRestaurants.includes(restaurant)}
setClicked={() => {
if (!clickedRestaurants.includes(restaurant)) {
setClickedRestaurants([...clickedRestaurants, restaurant])
}
}}
/>
);
})}
</div>
</Route>
);
}
function Button({ restaurant, hasBeenClicked, setClicked }) {
return (
<>
<Link href={`/restaurant/${restaurant}`} onClick={setClicked}>
<button>{restaurant}</button>
</Link>
<p>{restaurant} has {hasBeenClicked ? "" : "not "}been clicked</p>
</>
);
}

React - Show only the clicked user

In the following app, I'm accessing the random user API and show a list of 12 users.
App.js
import React, { useState } from 'react'
import UserList from './components/UserList'
const App = props => {
const [id, setID] = useState(null)
console.log(`Passed variable to App.js is: ` + id)
return (
<>
<UserList setID={setID} />
</>
)
}
export default App
UserList.js
import React, { useState, useEffect } from 'react'
import axios from 'axios'
const UserList = ({ setID }) => {
const [resources, setResources] = useState([])
const fetchResource = async () => {
const response = await axios.get(
'https://api.randomuser.me/?results=12'
)
setResources(response.data.results)
}
useEffect(() => {
fetchResource()
}, [])
return (
<ul>
{resources.map(item => (
<li key={item.name.first}>
<div>
<h2>{item.name.first} {item.name.last}</h2>
<button
onClick={() => setID(item.login.uuid)}
>
Details
</button>
</div>
</li>
))}
</ul>
)
}
export default UserList
The above code is working. But now I want that if I click on the button for any of those listed users, only that user get showed.
How can I do that?
The response JSON looks like this:
Easiest way would be to apply a filter on your ressources variable to only display the user with selected uuid.
To do that, first you need to share selected id with UserList component:
App.js
<UserList id={id} setID={setID} />
Then update UserList accordingly:
UserList.js
const UserList = ({ id, setID }) => {
return (
<ul>
{ resources
.filter(user => Boolean(id) ? user.login.uuid == id : true )
.map(item => (
<li key={item.name.first}>
<div>
<h2>{item.name.first} {item.name.last}</h2>
{ Boolean(id) ?
<button onClick={() => setID(null)}>
Hide
</button>
:
<button onClick={() => setID(item.login.uuid)}>
Details
</button>
}
</div>
</li>
)
}
</ul>
)
}
That way, you will only display the select user in you <ul>. To unselect your user, just call setID(null)
Show user profile instead of list
If that solution work to filter your list, I guess you might want to adapt your page to show all details from your user. Next step would be to implement multi pages using react-router-dom with a url container your user uuid.
You can look at the url-params example which might be exactly what you are looking for.
Here's a slightly detailed option that extends beyond a single component but more easy to scale on account of modularity.
Create a new react component in a new file say, UserDetails.js
Now you need a way to navigate to this new page when the button is clicked.
So in your App.js you need a router like
import { BrowserRouter, Switch} from 'react-router-dom'
Then in your App.js file wrap all your components in the router:
class App extends Component {
render() {
return (
<BrowserRouter>
<div className="App">
<Switch>
<Route exact path="/user-list" component={UserList} />
<Route exact path="/detail" component={UserDetails}/>
</Switch>
</div>
</BrowserRouter>
);
}
}
export default App;
Now you are ready to navigate to the user details page, when the button is clicked. So add a function like goToDetails like:
<button onClick={() => goToDetails(item)}>
Next define the function that navigates to the next page
goToDetails(item) {
this.props.history.push('/detail', {selectedUser:item:});
}
The history prop is available above because we earlier wrapped the entire app in BrowserRouter.
In the details page, you get the selectedUser details as a prop:
const selectedUser = this.props.location.state.selectedUser;
Now you can render it however you want.

Resources