How to make a "global" component render content based on url route? - javascript

I have a FAQ button that's fixed onto the bottom right corner of the screen (it's on every screen of the website), when clicked a modal pops out displaying frequent asked questions and their respective answers, I'd like the modal to display different content based on the url that the user is on.
For example: if the user is on www.example.com/signup then the modal would render the content specific to the sign up process, and when the user is on www.example.com/shop only the faq's related to shopping should appear. This should be taking into account the first part of the url params, so if a user goes to www.example.com/shop/294594 (or any other route that stars with /shop) then the modal still displays the same content as for www.example.com/shop.
Is there any good way to to this with react-router (or any other alternative)? I did some research into react-router useParams hook but I'm not sure if it's the go-to solution since I'm just a beginner in routing. Thanks for your time!

You can create a FAQ component like this https://codesandbox.io/s/react-router-tgh8c?file=/components/FAQ.js
import React from "react";
import { useLocation } from "react-router-dom";
const SignupFaq = () => <p>Sign up questions</p>;
const ShopFaq = () => <p>Shop questions</p>;
const faqs = {
signup: {
component: <SignupFaq />
},
shop: {
component: <ShopFaq />
}
};
function FAQ() {
const { pathname } = useLocation();
const { component } = faqs[pathname.split("/")[1]];
const [modal, showModal] = React.useState(false);
return (
<React.Fragment>
<button onClick={() => showModal(!modal)}>FAQ</button>
<div className="modal">{modal && <div>{component}</div>}</div>
</React.Fragment>
);
}
export default FAQ;

Related

ReactJS - Component with optional props inside React Context

In my app, there is a bottom sheet in the top level of my navigation system. As it is showed/hidden when the user interacts with the elements of other deeper screens inside the navigation system, I have had to wrap it inside a React Context. Take a look at this idea.
Now, imagine that this context is rendering my custom bottom sheet component:
function MessageOptions({ onDeleteButtonPress, onReportButtonPress, ... }) {
...
const handleOnDeleteButtonPress = () => {
...
onDeleteButtonPress?.();
}
const handleOnReportButtonPress = () => {
...
onReportButtonPress?.();
}
...
return (
<View style={globalStyles.overlayed} pointerEvents="box-none">
<OptionsBottomSheet
ref={menuRef}
snapPoints={[155, 0]}
initialSnap={1}
topItems={getTopItems()}
bottomItems={getBottomItems()}
onCloseEnd={onCloseEnd}
/>
</View>
);
}
As you can see, it is receiving two optional props, "onDeleteButtonPress" and "onReportButtonPress".
My idea is to consume the context which renders this custom bottom sheet component, in all my chat bubbles, just to open the bottom sheet when they are long pressed.
This is the context provider:
import React, { createContext, useRef } from "react";
import useStateWithCallback from "../../hooks/useStateWithCallback";
import MessageOptions from "../../components/Messaging/Options/MessageOptions";
const MessageOptionsContext = createContext(null);
export default MessageOptionsContext;
export function MessageOptionsProvider({ children }) {
...
return (
<MessageOptionsContext.Provider
value={{
open,
close,
}}
>
{children}
<MessageOptions
ref={messageOptionsRef}
message={message}
// onReportButtonPress={onReportButtonPress}
// onDeleteButtonPress={onDeleteButtonPress}
onCloseEnd={handleOnCloseEnd}
/>
</MessageOptionsContext.Provider>
);
}
When the user presses the delete button, I will be removing the specific message from the screen's messages list state.
How can I pass the methods from the parent (ChatScreen) of the consumer (Bubble) to the provider (MessageOptionsProvider)?
This is the flow of my app:
ChatScreen --> (contains the messages list (array) state)
|
|
---> BubbleList
|
|
---> Bubble (consumes the bottom sheet context)
I am doing this to avoid repeating my self in different routes of my app, but for being transparent, I am a little stuck in this use case.
Any ideas? It seems to be impossible?

Show a Modal on react functional component using redux toolkit

I have a dashboard page that shows a table list of all the posts, for each post I have and an edit button.
I'm trying to pop a modal when the edit button is clicked.
So I created a Modal component, which is rendered by the Dashboard component (this is a high order component equal to the App compo)
and I added a modal slice with redux toolkit and I successfully managed to change the modal state when the edit button is clicked but the modal doesn't show up.
I hope that I was thorough enough with what I'm trying to achieve, I also hope that you will help me guys, and now I'll share with you some of the code.
EditPostModal.jsx
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { makeStyles } from '#material-ui/core/styles'
import Modal from '#material-ui/core/Modal'
import editPostSlice, {
getPostToEditModal,
} from '../../store/slices/editPost'
const useStyles = makeStyles((theme) => ({.....}))
export default function SimpleModal() {
const classes = useStyles()
const modal = useSelector(getPostToEditModal)
const dispatch = useDispatch()
console.log('HEYYYY', modal) // modal is undefined
const handleClose = () => {
dispatch(editPostSlice.actions.closeModal())
}
if (!modal) return null
return (
<Modal
className={classes.modal}
open
onClose={handleClose}
aria-labelledby="simple-modal-title"
aria-describedby="simple-modal-description"
>
<h1>I AM THE MODAL</h1>
</Modal>
)
}
The first step to debug is to check if the modal will open without redux toolkit slice.
Also, can you confirm that the modal variable always return something other than a falsy value?

How to display a particular component with different API data on different pages

I am new to react and there is this challenge that i am having,
I have slider created a component
import React from 'react'
import NextEvent from '../nextEvent/NextEvent'
import './slider.css';
function Slider(props) {
const {id, image, sub_title, title} = props;
return (
<main id='slider'>
<div className="slide" key= {id}>
<div className="slide-image">
<img src={image} alt="slider-background"/>
</div>
<h1>{title} </h1>
<h5>...{sub_title}</h5>
</div>
<div className="event-countdown">
<NextEvent/>
</div>
</main>
)
}
export default Slider
I need to have this banner component on almost all my pages, and on each of the pages, it comes with a
different information (image, title, subtitle)
the backend guy sent the api and i consumed, but the problem is that, if i consume the api on the
component directly, all the pages will have the same info on the banner component which is not what i want,
also consuming the API on the homepage seemed like it was not the right thing to do, so i created another component which
collects the Api and i then added that new component to my homepage.
now my question goes:
did i do the correct thing ?
if correct, does it mean i have to create new corresponding components that will receive the APi for
each page i want to display the banner just like i did for the homepage?
will i have to as the backend guy to create different apis for each of the pages in which the
component is to be displayed
if no please help me with an efficient way which i can inject data coming from the backend into a
component which will be displayed on different pages with different data
this is the new component i created for the APi consumption
import React, {useState, useEffect } from 'react'
import NextEvent from '../../components/nextEvent/NextEvent'
import axios from "axios";
import '../../components/slider/slider.css';
const sliderUrl = "*************************************"
function HomeSlider(props) {
const [sliderData, setSliderData] = useState([]);
const { image, sub_title, title} = props;
const getSliderContents = async () => {
const response = await axios.get(sliderUrl);
const content = response.data;
setSliderData(content);
}
useEffect(() => {
getSliderContents();
}, [])
// console.log("slider", sliderData)
if(sliderData) {
return (
<main id='slider'>
{sliderData.map((item) => {
return (
<div className="slide" key= {item.id}>
<div className="slide-image">
<img src={item.image} alt="slider-background"/>
</div>
<h1>{item.title} </h1>
<h5>...{item.sub_title}</h5>
</div>
)
})}
<div className="event-countdown">
<NextEvent/>
</div>
</main>
)
}
}
export default HomeSlider
this is the Homepage i displayed it
function HomePage() {
return (
<div>
<NavBar/>
<SecondaryMenu/>
<HomeSlider />
<FeaturedBox />
Please any help is appreciated, i have search all over but no one explains how to display
component with different data on different pages
So i just wanted to get back on this, i figured i have to setup a service point where i call the api and then consume the endpoints on each page as desired

How to get previous url in react gatsby

I am pretty much familiar with the React.js but new to Gatsby.
I want to detect the previous page URL in Gatsby?
You can pass down state using the Link component:
import React from 'react';
import { Link } from 'gatsby';
const PrevPage = () => (
<div>
<Link
to={`/nextpage`}
state={{ prevPath: location.pathname }}
>
Next Page
</Link>
</div>
)
const NextPage = (props) => (
<div>
<p>previous path is: {props.location.state.prevPath}</p>
</div>
);
Then you have access to prevPath from this.props.location.state in the next page.
Full credit to #soroushchehresa's answer — this answer is just extras built upon it.
Gatsby will throw error during production build, since location is not available during server-side rendering. You could get around it by checking for window object first:
class Page extends React.Component {
state = {
currentUrl: '',
}
componentDidMount() {
if (typeof window == 'undefined') return
this.setState({ currentUrl: window.location.href })
}
render() {
return (
<Link to="..." state={{ prevUrl: this.state.currentUrl }}>
)
}
}
But this requires us to implement this on every page, which is tedious. Gatsby has already set up #reach/router for server-side rendering, so we can hook into its location props. Only router components get that props, but we can use #reach/router's Location component to pass it to other components.
With that, we can write a custom Link component that always pass previous url in its state:
// ./src/components/link-with-prev-url.js
import React from 'react'
import { Location } from '#reach/router'
import { Link } from 'gatsby'
const LinkWithPrevUrl = ({ children, state, ...rest }) => (
<Location>
{({ location }) => (
//make sure user's state is not overwritten
<Link {...rest} state={{ prevUrl: location.href, ...state}}>
{ children }
</Link>
)}
</Location>
)
export { LinkWithPrevUrl as Link }
Then we can import our custom Link component instead of Gatsby's Link:
- import { Link } from 'gatsby'
+ import { Link } from './link-with-prev-url'
Now each Gatsby page component will get this previous url props:
const SomePage = ({ location }) => (
<div>previous path is {location.state.prevUrl}</div>
);
You might also consider creating a container that store state for the client side & use the wrapRootElement or wrapPageElement in both gatsby-ssr.js and gatsby-browser.js.
These answers are partially correct. If you set state using link api then the state persists in browser history.
So if you go from Page1 to Page2 then the eg the state.prevUrl will correctly be set to Page1
But if the you go to Page3 from Page2 and then do a browser back then the state.prevUrl will still be Page1 which is false.
Best way I found to deal with this is to add something like this on the gatsby-browser.js
export const onRouteUpdate = ({ location, prevLocation }) => {
if (location && location.state)
location.state.referrer = prevLocation ? prevLocation.pathname : null
}
this way you will have always the previous url available on location.
I resolved my problem with the below piece of code. Here is the ref link https://github.com/gatsbyjs/gatsby/issues/10410
// gatsby-browser.js
exports.onRouteUpdate = () => {
window.locations = window.locations || [document.referrer]
locations.push(window.location.href)
window.previousPath = locations[locations.length - 2]
}
Now you can get previousPath can be accessed from anywhere.

How to only render modal in the initial route of the stacknavigator?

The initial screen of my stacknavigator has a modal that shows up upon the occurrence of a certain event ... problem is when I navigate to other screens the modal still shows up when the event is triggered. I want it only to show up when the initial route is showing up.
Maybe you can check this.props.navigation.state.routeName so that you know if it's initial screen or not.
I got this from this document.
https://reactnavigation.org/docs/navigators/navigation-prop
How about using state?
import React, { useEffect,useState } from "react";
const MainScreen = (props) => {
const [seenReminder, setReminder] = useState(false);
useEffect(()=>{
if (!seenReminder){
props.navigation.navigate('reminder')
setReminder(true)
}
}, [seenReminder]);
return (
<View ></View>
);
};
export default MainScreen;

Categories

Resources