Passing data between Router Components in React - reactjs

I have a react-router-dom based application where I currently have two screens: one for the initial setup and a second one that needs some information from the first. That information is stored in constants in the Home page's code, and I would like to know if there is a method for sharing the variables between them, or maybe passing them into the second in some way. The code and tree below provide a basic run-down of how I have it set up.
Structure:
App
|
Routes
|
BrowserRouter
____________________|____________________
| |
Route Route
Home ———— history.push("/in-game") ———> InGame
(+data)
Home code snippet:
const history = useHistory();
const toGame = () => history.push('/in-game');
const [colors, setColors] = useState();——————————
const [map, setMap] = useState();————————————————|—————————data I'd like to share with the InGame component
const [myColor, setMyColor] = useState();————————
Really hoping I can get a hand with this. Thanks!

you can use history object for this.
to change route from "home" to "ingame" with additional data to be passed into "ingame",
Home.js
const someEventHandler = event => {
history.push({
pathname: '/in-game',
state: { map: map }
});
};
to access this in "InGame",
InGame.js
const location = useLocation();
console.log(location.state.map); //map value is available here
try above. for more on history, https://reactrouter.com/web/api/history refer these docs from react-router.

Does this help
<Router>
<Switch>
<Route
path="/in-game/:data/:data2"
render={(props) => <InGame {...props} />}
/>
</Switch>
</Router>
This implies that you can pass two arguments in the URL in-game as a get parameter. These 2 params can be parsed in InGame component as this.props.match.params
To call this URL anywhere, you can use (example)
Click me
or
history.push("/in-game/param1/param2")
Refer this post: How to access 'this.props.match.params' along with other props?

Related

What am I doing wrong with React context?

Quick context, I am making a character-building app (think D&D Beyond). Users can log in, use the builder to generate a character, and then later recall and level-up that character. I'm hosting the data on MongoDB and using Realm Web for CRUD on the database. Based on this guide, I'm using React Context to keep track of the logged-in user as they navigate the site. I'm trying to create a widget in the NavMenu that will allow me to see the logged-in user for dev purposes (later, I can make a "Welcome, [name]!"-type greeting), but I keep getting the error that "mongoContext.user" is null. This makes sense, as the Context is set to null with useState, but as soon as the page renders there is an init() function which anonymously logs in the user if they aren't already. I can't seem to understand why this is happening, even after looking at 4 different articles on how to use React Context. Code Snippets below:
App.js
const [client, setClient] = useState(null);
const [user, setUser] = useState(null);
const [app, setApp] = useState(new Realm.App({id: process.env.REACT_APP_REALM_APP_ID}));
//Anonymously logs in to mongoDB App instance on page render
useEffect(() => {
const init = async () => {
if (!user) {
setUser(app.currentUser ? app.currentUser : await app.logIn(Realm.Credentials.anonymous()));
}
if (!client) {
setClient(app.currentUser.mongoClient('mongodb-atlas'));
}
}
init();
}, [app, client, user]);
//Function to render a given component and pass it the mongoContext prop
const renderComponent = (Component, additionalProps = {}) => {
return (
<MongoContext.Consumer>
{(mongoContext) => <Component mongoContext={mongoContext} {...additionalProps} />}
</MongoContext.Consumer>
);
}
return (
<MongoContext.Provider value={{app, client, user, setClient, setUser, setApp}}>
<Layout>
<Switch>
<Route exact path="/" render={() => renderComponent(Home)} />
... other Routes ...
</Switch>
</Layout>
</MongoContext.Provider>
);
}
As outlined in the guide, each Route receives a render function which wraps it in the Context.Consumer component and sends context. I couldn't figure out if this was possible with the Layout component (since it is itself a component wrapper), so instead I used React.useContext() inside the component I need context for: (the component relationship is Layout -> NavMenu -> UserDetail)
UserDetail.jsx
const UserDetail = (props) => {
const mongoContext = React.useContext(MongoContext);
return (
<div>
<h1>Logged in as: {mongoContext.user.id}</h1>
</div>
);
}
This is the component which gives the "mongoContext.user is null" error. The other components seem to use context just fine, as I have done some Create operations with the anonymous user. I can see that it probably has something to do with how the other components are rendered with the Consumer wrapper, but according to every guide I've read, the React.useContext in the UserDetail component should work as well. Thanks for any help, this has completely stymied the project for now.

Create wrapper to check user role in App.js component routes

I know the question is very general but I cannot find the correct way and terms to search this online to see how it can be done.
My problem is that I have an app that users log in and then a user role is added to a context element (like if the user is admin or simple user etc).
I can restrict access to the components by checking the user role and redirect. somerhing like
return user==="admin"?<div>...</div>: "redirect to no access component"
Is a way to create a wrapper that will wrap every Route in the App.js and perform this check instead.
normally you should restrict the access in the router of your application.
but to anwser the question you can create a hooks that checks if user is admin or else redirect to the no access component using react-router:
const useCheckUserPermission = (permission) => {
// get the user from the context
useEffect(() => {
if(user === permission) // redirect to no access component
}, [])
return [];
}
then you can use it like this:
const Component = (props) => {
useCheckUserPermission("admin");
return // return your component
}
If its a page then you can follow the following Approach:
While attaching Routes to Router you can create custom Route component which will check the condition.
Like:
import { Route } from "react-router-dom";
const PrivateRouteAdmin=({path,component:Component...rest})=>{
const user = useContext(AuthContext).userType;// any global state which you have used
return user==='admin'?
<Route path={path} {...rest}
render={(props)=>{
return <Component {...props} />
}}/>: <Redirect to={"/"}/>
}
You can use it in you code as follows:
<BrowserRouter>
<PrivateRouteAdmin exact path={'/protected'} component={ProtectedPage} />
</BrowserRouter>
If you want to apply some condition on specific page element, then you can use the approach which you are following otherwise make another component for that purpose too like:
const AdminContent = ({children)=> {
const user = useContext(AuthContext).userType;// any global state which you have used
return user==='admin'?children:<Redirect to={"/"}/>
}
and you can call the component anywhere you want like:
<AdminContent> <div>protected</div></AdminContent>

Move between pages without losing pages content - React JS

I'm trying to navigate between pages without losing pages data. For instance, I have a page that contains many input fields, if a user filled all these fields and tried to move to another page and return back to the first one, all the inputs are gone. I am using react-router-dom but didn't find out a way to prevent that.
What I've done till now :
import { Route, Switch, HashRouter as Router } from "react-router-dom";
<Router>
<Switch>
<Route path="/home" exact component={Home} />
<Route path="/hello-world" exact component={HellWorld} />
</Switch>
</Router>
Home Component :
navigateToHelloWorld= () => {
this.props.history.push('/hello-world')
};
Hello World Component :
this.props.history.goBack();
I don't know that that can be supported in such generality. I would just store all state variable values in localStorage, and restore from there when values are present on component render (when using useState then as the default value). Something like this:
const Home = () => {
const [field, setField] = useState(localStorage.field || '');
const handleUpdate = (value) => {
setField(value);
localStorage.field = value;
}
// also add a submit handler incl. `delete localStorage.field`;
return ... // your input fields with handleUpdate as handler.
}
Generally, all you need to do is to store your data in someplace, either a component that doesn't unmount, like the component you are handling your routes in, which is not a good idea actually but it works!
another way is to use some kind of state manager like 'mobx','redux','mst', or something which are all great tools and have great documentation to get you started
another alternative is to store your data in the browser, for your example session storage might be the one to go for since it will keep data until the user closes the tab and you can read it in each component mount.

Passing this.props.history.push to attribute in one variable in another component. REACT JS

I created one password form that needs pass values to one variable localized in another js.
The Scope is:
App.js
|
|
|----- Login.jsx
|
|
|_____ Home.jsx
|----- import { getMarvelCharacters } from '../lib/apiCalls';
App.js
const Home = lazy(() => import('./Components/Home'));
const Login = lazy(() => import('./Components/Login'));
const App = () => (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route exact path="/" component={Login}/>
<Route path="/home" component={Home}/>
</Switch>
</Suspense>
</Router>
);
Login.jsx
handleSubmit = event => {
//The alert show both value ok..
alert('publickey is: ' + this.state.publickey + ' and privatekey is: ' + this.state.privatekey);
event.preventDefault();
try {
this.props.history.push({
pathname: '/home',
state: {
publickey: this.state.publickey,
privatekey: this.state.privatekey,
}
})
} catch (e) {
alert(e.message);
}
}
But I need take this values e put in apiCalls.js
const PUBLIC_KEY = " here ";
const PRIVATE_KEY = " here ";
Please, what's the best way to make it
you can use Redux to push the data from your login component to the redux store and use it in another component inside of the application
and if you don't want to use Redux, then you have to create the functions inside of the main root component (which is usually App.js) and pass those functions inside the child components using props, the children will propagate the values to the root component or you can create the functions inside of the children and propagate the values to the root component (depends on your use case of each component like if this function is going to be done for one component or all components)
to reuse those values, you pass the root component's state as props inside of the child
hope my answer helped a bit

fetch the right data listening to the URL in react

I use react-router-dom to navigate through my components.
When I redirect to a detail component I give an item.name at the back of the URL.
const selectedBeerID = '/beerItem/' + this.props.beers.selectedBeerItem.name;
return <Redirect to={selectedBeerID}/>;
so the url will be like http://localhost:3000/beerItem/newName I would like to get the last parameter "newName" in my redirect page, so I'am able to fetch the right BeerItem from my back-end in my detail component. Can someone tell me a good way to achieve this? thanks a lot!
I think you’re looking for URL params. From the docs of react router 4, with customizations for your case:
<Route path="beerItem/:newName" component={Child} />
const Child = ({ match }) => (
<div>
<h3>{match.params.newName}</h3>
</div>
);
Please let me know if you need additional information.
If you have something like that in your Router (and you should):
<Route exact path={`/beerItem/:itemName`} component={BeerItemComponent} />
then you can access route params inside BeerItem component by
componentDidMount() {
const {match: { params: { itemName } } } = this.props
console.log(itemName) // do something with it.
}
NOTE that BeerItemComponent should be wrapped with withRoute HOC to have access to route params or use mapStateToProps.
Here is useful link from React Router docs.

Resources