ReactJS: Pass parameter from rails to react router to component - javascript

I am trying to pass a value from the render function to the component:
= react_component('App', props: {test: 'abc'}, prerender: false)
Routes.jsx
<Route path="/" component={App} >
App.jsx (component)
class App extends React.Component {
render() {
return (
<Header test={this.props.test}>
</Header>
{this.props.children}
<Footer />
);
}
}
App.propTypes = { test: PropTypes.string };
There does not seem to be a coherent answer to this complete flow.
I have tried the following:
<Route path="/" component={() => (<App myProp="value" />)}/>
But this still does not answer the question of picking up the value provided by the initial render call(react_component)

Looking for an end to end answer on how to pass a parameter from the
"view" to the "react router" to the "component"
We will start from the view:
<%= react_component('MyRoute', {test: 123}, prerender: false) %>
Now we will create a component that holds our route:
class MyRoute extends Component{
constructor(props){
super(props)
}
render(){
return(
<Switch>
<Route path="/" render={() => <App test={this.props.test} />} />
<Route path="/login" component={Login} />
</Switch>
)
}
}
As you can see, we passed the test prop from the Route component to the App component. Now we can use the test prop in the App component:
class App extends Component{
constructor(props){
super(props)
}
render(){
return(
<h1>{this.props.test}</h1>
)
}
}

<Route path="/" render={attr => <App {...attr} test="abc" />} />

In Router v3 you would do something like this
Wrap your App component under withRouter like this
import { withRouter } from 'react-router';
class App extends React.Component {
render() {
return (
<Header test={this.props.test}>
</Header>
{
this.props.children &&
React.clone(this.props.children, {...this.props} )}
<Footer />
);
}
}
App.propTypes = { test: PropTypes.string };
export const APP = withRouter(App);
And construct your routes like this...
<Route path="/" component={APP}>
<Route path="/lobby" component={Lobby} />
<Route path="/map" component={GameMap} />
...
</Route>
So your child routes will be rendered inside the APP children property an the props will be passed down to them.
Hope this helps!

Related

Why am I getting "Objects are not valid as a React child" error?

I have a lot of files, but I think the problem is coming from my authentication component in React. I basically want to only display a page if the user is logged in otherwise I want to the user to be redirected.
react-dom.development.js:14887 Uncaught Error: Objects are not valid as a React child (found: object with keys {$$typeof, type, compare, WrappedComponent}). If you meant to render a collection of children, use an array instead.
requireAuth.js
// function that can wrap any component to determine if it is authenticated
import React, { Component } from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { push } from "#lagunovsky/redux-react-router"; // not sure if this correct
export default function requireAuth(Component) {
class AuthenticationComponent extends React.Component {
constructor(props) {
super(props);
this.checkAuth();
}
componentDidUpdate(prevProps, prevState) {
this.checkAuth();
}
checkAuth() {
// if not authenticated then it is redirected
if (!this.props.isAuthenticated) {
const redirectAfterLogin = this.props.location.pathname;
this.props.dispatch(push(`/login?next=${redirectAfterLogin}`));
}
}
// if authenticated then renders the component
render() {
return (
<div>
{this.props.isAuthenticated === true ? (
<Component {...this.props} />
) : null}
</div>
);
}
}
AuthenticationComponent.propTypes = {
isAuthenticated: PropTypes.bool.isRequired,
location: PropTypes.shape({
pathname: PropTypes.string.isRequired,
}).isRequired,
dispatch: PropTypes.func.isRequired,
};
// checks isAuthenticated from the auth store
const mapStateToProps = (state) => {
return {
isAuthenticated: state.auth.isAuthenticated,
token: state.auth.token,
};
};
return connect(mapStateToProps)(AuthenticationComponent);
}
App.js
class App extends Component {
render() {
return (
<div>
<Root>
<ToastContainer hideProgressBar={true} newestOnTop={true} />
<Routes>
<Route exact path="/" element={<Home />} />
<Route path="/closet" element={requireAuth(Closet)} />
<Route path="*" element={<NotFound />} />
</Routes>
</Root>
</div>
);
}
}
I have done some digging but I can't find a problem like this.
The error is because on this line:
<Route path="/closet" element={React.createComponent(requireAuth(Closet))} />
You're passing the actual class definition to the element prop and not an instance of the class (which would be the React component). To fix this, you can use React.createElement:
class App extends Component {
render() {
return (
<div>
<Root>
<ToastContainer hideProgressBar={true} newestOnTop={true} />
<Routes>
<Route exact path="/" element={<Home />} />
<Route path="/closet" element={React.createElement(requireAuth(Closet))} />
<Route path="*" element={<NotFound />} />
</Routes>
</Root>
</div>
);
}
}
Because Route's element props need a ReactNode,But requireAuth(Closet)'s type is () => React.ReactNode, you can change your App.js like this:
const AuthComponent = requireAuth(Closet);
class App extends Component {
render() {
return (
<div>
<Root>
<ToastContainer hideProgressBar={true} newestOnTop={true} />
<Routes>
<Route exact path="/" element={<Home />} />
<Route path="/closet" element={<AuthComponent />} />
<Route path="*" element={<NotFound />} />
</Routes>
</Root>
</div>
);
}
}

Passing function prop to child component (via Link and Route components)

I'm using pure React with a Rails API backend.
I am fetching data from API and storing state in my Trips component. I have a Link component where I am able to pass the state to my NewTrip component, however <Link> does not allow me to pass functions.
I am able to pass functions to NewPage via render method on the Route component located at './routes/Index'.
But how do I pass the function from my Trips component? It's so much easier when passing as props to the component, the Router seems to be in the way!
'routes/Index.js'
export default (
<Router>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/trips" exact component={Trips} />
<Route path="/trip" render={(routeProps)=><NewTrip {...routeProps} />}/>
<Route path="/trip/:id" exact component={Trip} />
<Route path="/trip/:id/cost" exact component={NewCost} />
<Route path="/trip/:id/edit" exact component={EditTrip} />
</Switch>
</Router>
);
'components/Trips'
class Trips extends React.Component {
constructor(props) {
super(props);
this.state = {
trips: []
}
this.addTrip = this.addTrip.bind(this);
}
addTrip(trip) {
const {trips} = this.state;
trips.push(trip);
this.setState({ trips: trips});
}
render(){
return(
<Link
to={{
pathname: "/trip",
state: {trips: trips, onAddTrip={this.addTrip}} // not allowed,
// but I want to pass this function to the
// Component which is rendered by the Route in Index.js
}}
className="btn custom-button">
Create New Trip
</Link>
)
}
}
I think you should lift state up of the Trips component and have it in your 'routes/Index.js'. (it will need to be a component now, not just an export).
'routes/Index.js'
export default class Routes extends React.Component {
constructor(props) {
super(props);
this.state = {
trips: []
}
this.addTrip = this.addTrip.bind(this);
}
addTrip(trip) {
const {trips} = this.state;
trips.push(trip);
this.setState({ trips: trips});
}
render() {
return (
<Router>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/trips" exact component={Trips} />
<Route path="/trip" render={(routeProps)=>
<NewTrip addTrip={this.addTrip} trips={this.state.trips} {...routeProps} />
}/>
<Route path="/trip/:id" exact render={(routeProps)=>
<Trip addTrip={this.addTrip} trips={this.state.trips} {...routeProps} />
}/>
<Route path="/trip/:id/cost" exact component={NewCost} />
<Route path="/trip/:id/edit" exact component={EditTrip} />
</Switch>
</Router>
);
}
}
'components/Trips'
class Trips extends React.Component {
render() {
const trips = this.props.trips
return (
<Link
to={{
pathname: "/trip",
}}
className="btn custom-button">
Create New Trip
</Link>
)
}
}
It might be better to have the state even higher up in the App component, but you didn't provide that, so this has to do :)
You can pass functions using state in react router Link.
<Link
to={{
pathname: "/trip",
state: {trips: trips, onAddTrip: this.addTrip}
}}
className="btn custom-button">
Create New Trip
</Link>
And then in /trip, you retrieve and use the function like this:
this.props.location.state.addTrip();

History not accessible from react-router-modal

Hello I'm having a problem with setting 'react-router-modal' parentPath for the last used component
ModalRoute and ModalContainer are a part of
react-router-modal
App.js
class App extends Component {
render() {
return (
<main>
<Navbar />
<BrowserRouter>
<div>
<Switch>
<Route path="/main" component={ContentDefault} />
<Route path="/search" component={SearchScreen} />
<ModalRoute
path="/register"
parentPath="/"
component={RegisterScreen}
/>
<Route path="/contact" component={ContentDefault} />
<Route component={ContentDefault} />
</Switch>
<ModalContainer />
</div>
</BrowserRouter>
<Footer />
</main>
);
}
}
export default App;
SearchScreen.jsx
import React, { Component } from "react";
import { withRouter } from "react-router-dom";
class SearchScreen extends Component {
render() {
return (
<main>
<h1>SearchScreen</h1>
</main>
);
}
}
export default withRouter(SearchScreen);
For Example i'm on mainScreen then i move to the SearchScreen then i open modal from navbar. I need my modal to go back to the SearchScreen
I found couple of solutions which may help you.
You can try with this:
Create state prevPath
Add componentWillReceiveProps
Provide prevPath state to the parentPath with condition if prevPath is empty redirect me on route '/'
class App extends Component {
state = {
prevPath: ''
}
componentWillReceiveProps(nextProps) {
if (nextProps.location !== this.props.location) {
this.setState({ prevPath: this.props.location })
}
}
<ModalRoute
path="/register"
parentPath={this.state.prevPath || '/'}
component={RegisterScreen}
/>
This is one of the solutions we can try later another one if this doesn't help.

withRouter Does Not passes Route props to component

Here is my navigation component:
import React from 'react'
class Navigation extends React.Component {
constructor(props) {
super(props);
this.state = {
type: 'signUp', // or login
showModal: false,
isLoggedIn: false,
}
}
...some code
render() {
const { showModal, type, isLoggedIn } = this.state
console.log(this.props.location); // all problem is this, I'm not getting it in console
return(
...some more code
)
}
}
export default withRouter(Navigation)
And here is where it it been used in app.js
class App extends React.Component {
render () {
return(
<Router>
<Fragment>
<Navigation /> // <= right there
<Switch>
<Route exact path='/' component={HomePage}/>
<Route exact path='/search' component={HomePage}/>
<Route component={Lost} />
</Switch>
</Fragment>
</Router>
)
}
}
I want to get updated route props like match and location and history in my <Navigation /> component but I get it only when the first time that component mounts on the DOM, in my other components I update the route using window.history.pushState but I am not able to get route props from withRouter after link in the browser is been updated.
I update route with window.history.pushState because:
I could not find any way to update just link in the address bar without showing user or redirecting user to new component with React router DOM (am I doing it in right way or not?)
based on that I then use window.location.pathname to add some specific stylings to some components)
Also, I read the entirety of this and this but I could not solve this issue. What am I doing wrong?
withRouter gives you the closest <Route>'s route props, and since the Navigation component is not inside a Route you will not get the route props.
You could e.g. put the Navigation component on a Route outside of the Switch that will always be visible.
Example
class App extends React.Component {
render() {
return (
<Router>
<Fragment>
<Route path="/" component={Navigation} />
<Switch>
<Route exact path="/" component={HomePage} />
<Route exact path="/search" component={HomePage} />
<Route component={Lost} />
</Switch>
</Fragment>
</Router>
);
}
}

React. New Router after login

I have problem with Router in React, after login i change type state in Redux from 0 to 1, then i make switch in my App file, but i got error
Warning: [react-router] You cannot change <Router routes>; it will be ignored
This is my index.js, I want change all Route links if user is login (form with login work good and they change redux state type to 1):
#connect((store)=>{
console.log(store)
return {
typeUser: store.app.type
}
})
class App extends React.Component{
render(){
switch(this.props.typeUser){
case 0:{
return(
<Router history={browserHistory}>
<Route path={"/"} component={MainPage}></Route>
<Route path={"/login"} component={Login}></Route>
<Route path={"product/:nameProduct/:id"} component={ProductDetails}></Route>
</Router>
)
break;
}
case 1:{
return(
<Router history={browserHistory}>
<Route path={"/"} component={MainPageAfterLogin}></Route>
<Route path={"/login"} component={LoginAfterLogin}></Route>
</Router>
)
break;
}
}
}
}
const app = document.getElementById('app');
ReactDOM.render(<Provider store={store}>
<App/>
</Provider>,app);
You cannot change the Router but you can change the Routes configuration that you have , so you can setup the Routes like
class App extends React.Component{
render(){
return(
<Router history={browserHistory}>
{this.props.typeUser === 0? <User1/>: <User2/>}
</Router>
)
}
}
class User1 extends React.Component {
render() {
return (
<div>
<Route path={"/"} component={MainPage}></Route>
<Route path={"/login"} component={Login}></Route>
<Route path={"product/:nameProduct/:id"} component={ProductDetails}></Route>
</div>
)
}
}
class User2 extends React.Component {
render() {
return (
<div>
<Route path={"/"} component={MainPage}></Route>
<Route path={"/login"} component={Login}></Route>
</div>
)
}
}

Categories

Resources