Button don't passing data to another component. React - reactjs

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.

Related

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>
</>
);
}

why is my component getting rendered once but then failing on refresh

i am working on small react assignment,
following is my component code. So my component is getting rendered once but then it just fails.i'll attach the screenshots too, can some one please explain what is happening?is there an error in the code or is it because of some rate limiting in API i am using?
import React from 'react'
const Menu = ({events}) => {
console.log(events);
return (
<div>
{events.map((event)=>{
return( <div key={event.category}>
<h3>{event.category}</h3>
</div>)
})}
</div>
)
}
export default Menu
code working image
error on same code pic
parent component code
import React,{useState,useEffect} from 'react';
import './App.css';
import Menu from './components/Menu';
function App() {
const [isLoading,setISLoading] = useState(true);
const[events,setEvents] = useState()
const getEvents = async()=>{
const response = await fetch('https://allevents.s3.amazonaws.com/tests/categories.json');
const eventsData =await response.json()
setISLoading(false);
setEvents(eventsData);
}
useEffect(()=>getEvents(),[]);
return (
isLoading?<h1>Loading...</h1>:<Menu events = {events}/>
);
}
export default App;
May be the parent component of Menu which is supplying events is not using any loading state. So when the component is mounted and starts making ajax calls, events is undefined. You need to put a condition over there like this:
import React from 'react'
const Menu = ({events}) => {
console.log(events);
return events ? (
<div>
{events.map((event)=>{
return( <div key={event.category}>
<h3>{event.category}</h3>
</div>)
})}
</div>
) : null
}
export default Menu

Syncrohize react conditional rendering and typescript

In this component I trigger post loading using postsActions.getPost('1') and put it into the redux store. useSelector catches it and triggers PostPage rerender, now with header and button with onClickUse function attached that uses post.header along with the post object that it uses:
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { postsActions } from '../../store/Posts';
import styles from './PostPage.module.scss';
const PostPage = () => {
const dispatch = useDispatch();
const post = useSelector((state) => state.post);
const onClickUse = () => {
console.log(`See? I am used only when post is rendered`);
console.log(`So it's fine if I use it here: ${post.header}`);
}
useEffect(() => {
dispatch(postsActions.getPost('1'));
}, []);
return (
<div className={styles.container}>
{
post &&
<div>
<h1>{post.header}</h1>
<button onClick={onClickUse}>Click me</button>
</div>
}
</div>
);
};
export default PostPage;
The problem is that typescript yells inside onClickUse at me that post can be undefined. How do I then synchronize React conditional rendering functionality and typescript without hacks from this question, like !, as etc.
You can inline
<div className={styles.container}>
{
post &&
<div>
<h1>{post.header}</h1>
<button onClick={() => {
console.log(`See? I am used only when post is rendered`);
console.log(`So it's fine if I use it here: ${post.header}`);
}}>Click me</button>
</div>
}
</div>
or if you don't want inline functions in render, you should create a component with not falsy post in props and conditionally render it.
Typescript (in handler) knows nothing about render logic in your example

Components to navigate to another page with React Router

is it proper to write components like this? I want to redirect user to another page after click on button in my menu? What should i change here? Maybe use Link instead of button?
import React from 'react';
import { useHistory } from 'react-router-dom';
import './MyAccountButton.scss';
const MyAccountButton = ({ path }) => {
const history = useHistory();
const handleButtonClick = () => {
history.push(path);
};
return (
<div className="my-account-button">
<button type="button" onClick={() => handleButtonClick()}>
My account
</button>
</div>
);
};
export default MyAccountButton;
PS. Any advices how to unit test this component with jest and enzyme??

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