Passing props to props.children, receiving wrong props - javascript

App.js is the wrapper for my page, it's the layout enclosing the routes. Initially my routes were in App.js, but I want them in a higher order component so I can create routes which arent wrapped by App.
This is my current setup:
index.js
ReactDom.render(
<Provider>
<BrowserRouter>
<Route path='/' [...] /> // <-- Component which is NOT wrapped in App
<App>
<Route path='/about' component={About} /> // <-- I want About to receive the props from App
</App>
</BrowserRouter>
</Provider>
);
My App.js holds the Header, Sidebar and Footer component. App.js is the component I am exporting with connect(), I am mapping my redux state and actions with mapStateToProps and mapDispatchToPros in App.js
To pass the props down to, say, About, I tried different things with the same result, one of them being:
App.js
{React.cloneElement(this.props.children, {...this.props})}
However, the props I receive in About are completely different from the ones I am passing down. They are like this: (json)
{"match": {"path":"/about", "url": "/about", "isExact":true [...]}
So I am guessing those props come from the index.js, where my routes are. But how do I achieve what I am trying to do? Index.js is the component I am rendering the whole Application in. I am not exporting index.js.
Should I move the redux related stuff like mapStateToProps, mapDispatchToProps, connect(), ... to index.js? Or is there another solution?
Thanks!

Related

Using Router DOM with React.js TypeError: Cannot read property "name" of undefined

I am trying to implement the React Router on my React.js application but as soon as I added the Router to my program I started getting a weird error.
ReactDOM.render(
<BrowserRouter>
<App />
<Route path="/" component={Headline}></Route>
<Route path="/economy" component={Economics}></Route>
</BrowserRouter>,
This is where I am implementing my <Router> component in index.js. But for some reason as soon as I add this to my application, I get the error:
TypeError: Cannot read property 'name' of undefined
name is the parameter of one of my objects, userInput, that I am passing via props to my child component, "headline" from App.js
<Headline
userInput={this.state.userInput}
money={this.state.money}
displayFakeNews={this.state.displayFakeNews}
onPublish={this.handlePublish}
/>
Inside the headline.jsx file, I am calling a <button> tag with the text rendering {this.props.userInput.name} and it the error comes up.
As soon as I remove the <Router> component from the index.js file, everything starts working absolutely perfectly.
*Note - Yes I tried to put the <Router> component in my App.js as well. And no I did not forget to import it.
In your routes, you're declaring that the component to be rendered at route "/" is the Headline component, but you're not passing it any props. This means that whenever you visit the home page, the Headline component is likely trying to access unassigned properties.
From your question, I assume that you already have the Headline component being rendered in your <App /> component, and in this file, it is actually being passed the necessary props. If its already being rendered there, you don't need to use the <Route /> outside of the <App />. It's not clear the functionality you're looking for or how you've written your <App /> component, but I think what you should keep in mind is that the syntax you're using doesn't pass any props to <Headline /> from the route. If you actually want to pass those props, change
<Route path="/" component={Headline}></Route>
to
<Route path="/">
<Headline
userInput={this.state.userInput}
money={this.state.money}
displayFakeNews={this.state.displayFakeNews}
onPublish={this.handlePublish}
/>
</Route>
Passing the component to be rendered as a child of the route, so that you can actually pass it props.
Consult the React Router documentation for more information on how to use the <Route /> component.

reactjs props getting lost in Route creation

I'm attempting to pass a user's auth state down to components via a react-router-dom switch block (I believe I ought to look at implementing redux but that's a question for later).
There's a Home view that gets passed all the login information after the user authenticates, and I can see the authState object as a prop in the home component using react devtools:
import React from "react";
import Dashboard from "../Dashboard";
import {Switch, Route} from "react-router-dom";
import NoMatch from "../NoMatch";
function Home(props) {
// authState exists
return (
<Switch {...props}>
// authState exists
<Route exact path="/" render={(...props) => <Dashboard {...props} />} /> // authState gone
<Route component={NoMatch} />
</Switch>
);
}
export default Home;
after executing this it renders the Dashboard, and tracing down the component chain with react devtools I can see that the prop has been passed from the Home component to the Switch component successfully. However, once it gets to the Route the prop is gone, and the only props available in the Dashboard component are the history, location and match props.
Why are the props missing and what's the right way to pass them down?
Couple of improvements needed in your code:
Passing props to Switch component is unnecessary
No need to collect router props using the rest syntax only to spread them later
Main Problem:
props inside the function passed to render prop refers to the router props instead of the props passed to Home component. You are using the same identifier for two different things.
Solution
Use different names for router props and the props passed to Home component that you want to pass down to Dashboard component
function Home(props) {
return (
<Switch>
<Route
exact
path="/"
render={(routerProps) => <Dashboard {...routerProps} {...props} />}
/>
<Route component={NoMatch} />
</Switch>
);
}
Alternatively, if you don't need access to router props in the Dashboard component, remove the props parameter from the render prop function.
<Route
exact
path="/"
render={() => <Dashboard {...props} />}
/>
Now, you won't have access to router props inside the Dashboard component but the auth state of the user will be passed down to Dashboard component.
In the most recent versions of the react-router-dom you must replace render and component attributes with element. You cannot pass a callback function there in which there were specified the route props anymore.
Instead you can use a hook in your routed component:
const params = useParams();
to obtain the parameters.
See more in the official documentation.

React-router-dom goBack(), how to inherit prop from another js file?

My route path is in separated file that contains only routes, nothing more and this is the only file that I'm importing in it React-router-dom.
The thing is, I want to specify a button in another js file, that will do goBack() function but I don't want to import react-router-dom here, but inherit prop from the main js file.
So, I have added to my App.js this lines (this is the main file that only have routes)
constructor(props) {
super(props);
this.goBack=this.goBack.bind(this);
}
goBack(){
this.props.history.goBack();
}
And the button
<button onClick={this.goBack}>Go Back</button>
Is there a way to do it that way or I really need to import router-dom and add constructor to another file?
You don't need to import router-dom on your new file. You just need to pass down the history prop all the way to the button component. Alternatively, set the history prop in a context or redux store variable - hopefully your back button is not nested deep in your component tree. Tip: Try to have your component using router props shallow in your tree to avoid passing through router props.
My router is set up something like:
<Router>
<div>
<Switch>
<Public exact path="/" component={ConnectedLoginForm} {...props} />
<Route path="/recover-password" component={ConnectedRecoverPasswordForm} />
<Route path="/reset-password/:token" component={ConnectedResetPasswordForm} />
<Route path="/invite/:token" component={ConnectedAcceptInviteForm} />
<Public path="/login" component={ConnectedLoginForm} {...props} />
<Authenticated path="/:co_id" component={ConnectedMainView} {...props} />
</Switch>
</div>
</Router>
Thus I have access to history prop in my ConnectedMainView component.

Passing state between sibling components React Router v4

I want to pass state between various sibling components: Dashboard and all routes which contain /overall. Thea idea is that I can click a button on any of the pages which contain /overall in the URL and it gets the corresponding value and moves that value to the dashboard. I've been doing some reading around this subject however I'm struggling to understand which is the best implementation for my purpose.
index.js
import React from "react";
import { render } from "react-dom";
import Router from "./components/Router";
import registerServiceWorker from "./registerServiceWorker";
import "./css/style.css";
render(<Router />, document.getElementById("main"));
registerServiceWorker();
Router.js
import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import NameSelector from "./NameSelector";
import Dashboard from "./Dashboard";
import OverallGoals from "./OverallGoals";
import OverallShots from "./OverallShots";
import OverallPossession from "./OverallPossession";
import OverallResults from "./OverallResults";
import OverallFormations from "./OverallFormations";
const Router = () => (
<BrowserRouter>
<Switch>
<Route exact path="/" component={NameSelector} />
<Route exact path="/:gamerId/dashboard" component={Dashboard} />
<Route exact path="/:gamerId/overall/goals" component={OverallGoals} />
<Route exact path="/:gamerId/overall/shots" component={OverallShots} />
<Route
exact
path="/:gamerId/overall/results"
component={OverallResults}
/>
<Route
exact
path="/:gamerId/overall/possession"
component={OverallPossession}
/>
<Route
exact
path="/:gamerId/overall/formations"
component={OverallFormations}
/>
<Route component={NotFound} />
</Switch>
</BrowserRouter>
);
export default Router;
From my understanding, passing state through the Route wouldn't work as Route doesn't actually render anything. I need a parent component to hold state for both siblings however it doesn't make sense to make a component solely for managing state between the two components (or would it?).
What's the best practice for this and how would it fit into my codebase?
Also, I want to avoid Redux for now, as I will appreciate the technology more when I implement this solution in pure React.
Fairly new to React, I understand this code could be slightly more efficient.
EDIT: NameSelector is a component which holds an input field which once submitted push()es to a URL containing the input
Regards,
James.
I need a parent component to hold state for both siblings however it doesn't make sense to make a component solely for managing state between the two components (or would it?).
It does!
Pretty much, short of using Redux or the React Context API, you are going to need a common parent component that holds, shares, and manages updates to the state that you'd like your two sibling components to share.
If your component-tree is simple and short, and the sibling components are in close proximity (i.e. you don't need to pass the state up-and-down multiple tiers), then this configuration makes a lot of sense. Have a parent component that manages state, render the two siblings underneath it.
If the tree structure is (or will become) complex, with components and subcomponents separated from each other across multiple tiers, then start thinking about and investing in a Redux store.

React Router Route Params in Redux

Using redux and react router, I would like to access a route parameter on a component other than the one specified by the route.
I've got react-router, redux, and react-router-redux set up as follows and I would like to access the reportId parameter from my redux store.
const history = syncHistoryWithStore(browserHistory, store);
const store = createStore(reducers);
const routes = (
<Router history={history}>
<Route path="/" component={MainLayout} >
<IndexRoute component={Home} />
<Route path="reports">
<Route path=":reportId" component={ReportViewerContainer} />
</Route>
</Route>
</Router>
);
ReactDOM.render(
<Provider store={store}>{router}</Provider>,
document.getElementById('root')
);
I've tried hooking up react-redux-router to store the route state, but I've found that there is nothing useful stored inside of redux besides the full path to the active route. This has left me with the options of either parsing out the reportId from the path in redux, or creating my own custom action in redux and updating it with the componentWillReceiveProps lifecycle method inside of my ReportViewerContainer component. There must be a better way?
If you want to access the router parameter inside the component, the best way to achieve this by using withRouter
https://egghead.io/lessons/javascript-redux-using-withrouter-to-inject-the-params-into-connected-components
You will find the better understanding with above example.
If you want to access the router in the redux store, there's a HOC called withRouter (docs). If I remember correctly, this will pass any router state to your component as props.
The only change you need to make to your component would be this:
import { withRouter } from 'react-router'
export default withRouter(ReportViewerContainer);
So I think you can use context to fetch the location that will be passed through the Router. ( Refer that link on how it works.) This should give you a solid direction to work on.
This can also help you down the lane to work with internationalization as well.
class MyComponent extends React.Component {
static contextTypes = {
router: React.PropTypes.object
};
render(){
// By declaring context type here, and childContextTypes
// on the parent along with a function with how to get it,
// React will traverse up and look for the `router` context.
// It will then inject it into `this.context`, making it
// available here.
}
}

Categories

Resources