Carry ownProps deep react router - reactjs

I am trying to gain access to params in ownProps. Is there a way to solve this?
Current structure is
const Router = () => (
<router>
<Switch>
<Route path='/signup' component={Signup} />
<Route path='/browse' component={Browse}/>
<Route path='/detail/:id' component={Detail} />
</Switch>
</router>
)
i want ownProps to pass on to these pages in Detail
export default class App extends Component {
render() {
return (
<div>
<Info />
<Message/>
</div>
);
}
}

After messing around I found a way which is that I passed the required information as props to nested components.
export default class App extends Component {
render() {
return (
<div>
<Info idInfo={this.props}/>
<Message idInfo={this.props}/>
</div>
);
}
}
after this I got information in ownProps.

Related

React routing - Routes independent of parent routing files

I am defining the routes in react
Global ones in the app-routes.js file and other in their respective components.
app.js
render() {
return (
<div className="App-wrap">
<AppRoutes/>
</div>
); }
app-route.js
export class AppRoutes extends React.Component {
render() {
return (
<Router>
<Switch>
<Route path="/" component={LayoutComponent} />
</Switch>
</Router>
);
}
}
layout-component.js
I have placed my header here for navigation
export class LayoutComponent extends React.Component {
render() {
return (
<LayoutWrap>
<HeaderComponent> </HeaderComponent>
<LayoutRoutes />
</LayoutWrap>
);
}
}
layout.routing.js
export class LayoutRoutes extends React.Component {
render() {
return (
<Switch>
<Route path="/" exact >
<Redirect to="/users"/>
</Route>
<Route path="/users" name="Users" component={UserComponent} />
<Route path="/permissions" name="Permissions" component={PermissionComponent} />
</Switch>
);
}
}
Now the issue is, when i am defining my child routes, my child routes are dependent on parent
i.e. i have to write parent's previous url in child.
If i change my app-routes.js path from "" to layout my routing will not work.
<Route path="/layout" component={LayoutComponent} />
How to solve the issue?
When you are defining your child Routes, you need to prefix the parent route path before it for these to work. For this you can make use of match.path from props like
export class LayoutRoutes extends React.Component {
render() {
const { match } = this.props;
return (
<Switch>
<Route path={match.path} exact render ={(props) => <Redirect to="/users"/>} />
<Route path=`${match.path}/users` name="Users" component={UserComponent} />
<Route path=`${match.path}/permissions` name="Permissions" component={PermissionComponent} />
</Switch>
);
}
}

React router open modal

I'm new to react and redux world. I have a question regarding react router. Following is my App component.
class App extends React.PureComponent {
render() {
return (
<div>
<Switch>
<Route exact path="/" component={HomePage} />
<Route exact path="/callback" component={CallbackPage} />
<Route exact path="/list" component={List} />
<Route component={NotFoundPage} />
</Switch>
</div>
);
}
}
export default App;
My list class looks like this:
export class List extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
isOpen: false,
};
this.depositHandleToggle = this.depositHandleToggle.bind(this);
}
depositHandleToggle = () => {
this.props.dispatch(push(`${this.props.match.url}/deposit`));
}
render() {
const { match } = this.props;
return (
<div>
<Route path={`${match.url}/deposit`} component={Modal} />
<button onClick={this.depositHandleToggle}>Open Modal</button>
</div>
);
}
}
export default List;
So my question is: when i click the button in List container, why my router can't find this url 'localhost:xxx/list/deposit'? it renders the App component but it never goes back to List component. Is it possible to have the custom routes inside my list container? Am i doing something wrong? Please someone help me with this.
I hope you understand my question. Thanks in advance.
ANSWER:
I found the answer, the issue was in my App component list route. I was having the 'exact' keyword, that's why route inside my list component was not working. Following way is the correct way.
<Route path="/list" component={List} />
I hope this will help someone.
import { Link } from 'react-router-dom';
class App extends React.PureComponent {
render() {
return (
<div>
<Switch>
<Route exact path="/" component={HomePage} />
<Route exact path="/callback" component={CallbackPage} />
<Route exact path="/list" component={List} />
<Route exact path="/list/deposit" component={Modal} />
<Route component={NotFoundPage} />
</Switch>
</div>
);
}
}
export default App;
export class List extends React.PureComponent {
constructor(props) {
super(props);
}
render() {
const { match } = this.props;
return (
<div>
<Link to={'/deposit'}>Open Modal</Link>
);
}
}
export default List;
in that component you should handle modal open close.

Pass store to the layout component in react router redux?

I want to pass props and invoke actions in my Layout component. When I try to do it with the following code, only the initial state is passed. The changes in the redux store are not reflected.
This is the code to initiate the app.
ReactDOM.render(
<AppContainer>
<Provider store={ store }>
<ConnectedRouter history={ history } >
<Layout prop={store.getState()}>
<Route exact path='/' component={ Dashboard } />
<Route path='/workorder' component={ WorkOrder } />
<Route path='/programs' component={ Programs } />
<Route path='/departments' component={ Departments } />
<Route path='/users' component={ Users } />
<Route path='/fetchdata/:startDateIndex?' component={ FetchData } />
</Layout>
</ConnectedRouter>
</Provider>
</AppContainer>,
document.getElementById('react-app')
);
This is the code of the layout page.
interface LayoutProps{
prop: any;
}
export class Layout extends React.Component<LayoutProps, {}> {
public render() {
console.log(this.props);
return <div>
<Header/>
<div id="wrapper">
<NavMenu/>
{ this.props.children }
</div>
</div>;
}
}
My console log only prints the initial state all the time. I don't know why it is not getting updated. I have this problem only for layout. All the other components defined in the routes are connected using 'connect' and works properly.
Edit: This is the updated code:
ReactDOM.render(
<AppContainer>
<Provider store={ store }>
<ConnectedRouter history={ history } >
<Layout>
<Route exact path='/' component={ Dashboard } />
<Route path='/workorder' component={ WorkOrder } />
<Route path='/programs' component={ Programs } />
<Route path='/departments' component={ Departments } />
<Route path='/users' component={ Users } />
<Route path='/fetchdata/:startDateIndex?' component={ FetchData } />
</Layout>
</ConnectedRouter>
</Provider>
</AppContainer>,
document.getElementById('react-app')
);
This is the code in Layout:
export class Layout extends React.Component<any, {}> {
public render() {
console.log(this.props);
return <div>
<Header/>
<div id="wrapper">
<NavMenu/>
{ this.props.children }
</div>
</div>;
}
}
export default connect(
(state: ApplicationState) => state.users, // Selects which state properties are merged into the component's props
UsersStore.actionCreators.getCurrentUser // Selects which action creators are merged into the component's props
)(Layout) as typeof Layout;
But only props.children are available inside the Layout component. All the other props are undefined even though I pass state.users, UsersStore.actionCreators.getCurrentUser. Basically it is not taking anything from redux store (Even after any number of re-renders).
You need to be using react-redux's connect enchancer.
import { connect } from 'react-redux'
import { updateSomething } from 'actions'
class LayoutComponent extends React.Component {
render() {
return (
<div onClick={this.props.updateSomething}>
something: {this.props.something}
</div>
)
}
}
export Layout = connect((state) => {
return { something: state.something }
}, { updateSomething })(LayoutComponent)
edit: see here for more information

Multiple Layouts with React Router v4

I'm pulling my hair out trying to render multiple layouts with React Router v4.
For instance, I'd like pages with the following paths to have layout 1:
exact path="/"
path="/blog"
path="/about"
path="/projects"
and the following paths to have layout 2:
path="/blog/:id
path="/project/:id
Effectively what's being answered here but for v4: Using multiple layouts for react-router components
None of the other answers worked so I came up with the following solution. I used the render prop instead of the traditional component prop at the highest level.
It uses the layoutPicker function to determine the layout based on the path. If that path isn't assigned to a layout then it returns a "bad route" message.
import SimpleLayout from './layouts/simple-layout';
import FullLayout from './layouts/full-layout';
var layoutAssignments = {
'/': FullLayout,
'/pricing': FullLayout,
'/signup': SimpleLayout,
'/login': SimpleLayout
}
var layoutPicker = function(props){
var Layout = layoutAssignments[props.location.pathname];
return Layout ? <Layout/> : <pre>bad route</pre>;
};
class Main extends React.Component {
render(){
return (
<Router>
<Route path="*" render={layoutPicker}/>
</Router>
);
}
}
simple-layout.js and full-layout.js follow this format:
class SimpleLayout extends React.Component {
render(){
return (
<div>
<Route path="/signup" component={SignupPage}/>
<Route path="/login" component={LoginPage}/>
</div>
);
}
}
So, for this you should use render function (https://reacttraining.com/react-router/core/api/Route/render-func)
A really good article that helped me: https://simonsmith.io/reusing-layouts-in-react-router-4/
In the end you will be use something like this:
<Router>
<div>
<DefaultLayout path="/" component={SomeComponent} />
<PostLayout path="/posts/:post" component={PostComponent} />
</div>
</Router>
I solved this problem utilizing a bit of both of your solutions:
My Routes.js file
import BaseWithNav from './layouts/base_with_nav';
import BaseNoNav from './layouts/base_no_nav';
function renderWithLayout(Component, Layout) {
return <Layout><Component /></Layout>
}
export default () => (
<Switch>
{/* Routes with Sidebar Navigation */}
<Route exact path="/" render={() => renderWithLayout(Home, BaseWithNav)} />
{/* Routes without Sidebar Navigation */}
<Route path="/error" render={() => renderWithLayout(AppErrorMsg, BaseNoNav)} />
<Route path="/*" render={() => renderWithLayout(PageNotFound, BaseNoNav)} />
</Switch>
)
Base.js (where routes get imported)
export default class Base extends React.Component {
render() {
return (
<Provider store={store}>
<Router>
<Routes />
</Router>
</Provider>
)
}
}
Layouts
BaseWithNav.js
class BaseWithNav extends Component {
constructor(props) {
super(props);
}
render() {
return <div id="base-no-nav">
<MainNavigation />
<main>
{this.props.children}
</main>
</div>
}
}
export default BaseWithNav;
BaseNoNav.js
class BaseNoNav extends Component {
constructor(props) {
super(props);
}
render() {
let {classes} = this.props;
return <div id="base-no-nav">
<main>
{this.props.children}
</main>
</div>
}
}
export default BaseNoNav;
I hope this helps!
I know i am replying late but it's easy to do that, i hope it will helps to newbie.
i am using React 4
Layout.js
export default props => (
<div>
<NavMenu />
<Container>
{props.children}
</Container>
</div>
);
LoginLayout.js
export default props => (
<div>
<Container>
{props.children}
</Container>
</div>
);
Now finally we have our App
App.js
function renderWithLoginLayout(Component, Layout) {
return <LoginLayout><Component /></LoginLayout>
}
function renderWithLayout(Path, Component, Layout) {
return <Layout><Route path={Path} component={Component} /></Layout>
}
export default () => (
<Switch>
<Route exact path='/' render={() => renderWithLayout(this.path, Home, Layout)} />
<Route path='/counter' render={() => renderWithLayout(this.path, Counter, Layout)} />
<Route path='/fetch-data/:startDateIndex?' render={() => renderWithLayout(this.path, FetchData, Layout)} />
<Route path='/login' render={() => renderWithLoginLayout(Login, LoginLayout)} />
</Switch>
);

Route not rendering component when Url changes

I have the following ProductThumbnail component where as i click on the link it updates the URL and changes the route to redirect toward ProductDesc component. It generates the URL /productDescRedux/itemId properly.
const ProductThumbnail = (props) => {
const itemId = props.product
return(
<div>
<Link to={`/productDescRedux/${itemId.id}`}>
<div>
<h1>{props.product.headline}</h1>
<img src={props.product.images[0].imagesUrls.entry[1].url} alt="Thumbnail small pic"/>
<p></p>
</div>
</Link>
</div>
)
}
ProductThumbnail.propTypes = {
product: React.PropTypes.object
};
export default ProductThumbnail;
However despite URL changes, it does not call the component ProductDesc and i have to reload the page to display the component ProductDesc. Below the routes and the component ProductDesc
const Routes = function(){
return(
<Provider store={store}>
<Router history={ hashHistory }>
<Route path="/" component={ Container }>
<IndexRoute component={ Home } />
<Route path="/productredux" component={ App } >
<IndexRoute component={ ProductsGrid }/>
<Route path="/productDescRedux/:id" component={ ProductDesc } />
</Route>
<Route path="*" component={ NotFound } />
</Route>
</Router>
</Provider>
)
}
export default Routes;
const ProductDesc = ()=>({
render(){
return (
<div>
<h1>hello</h1>
<p>Yeah</p>
</div>
)
}
})
And here for completion the App component which uses connect() as well as the Main component
function mapStateToProps(state){
return {
products:state.products
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators(actionCreators,dispatch);
}
const App = connect(mapStateToProps,mapDispatchToProps)(Main);
export default App;
//in a different file
const Main = (props) => ({
render(){
return (
<div>
{React.cloneElement(props.children, this.props)}
</div>
)
}
})
export default Main;
So I don't see why when changing the URL , routing is not calling the component ProductDesc. any insight ?
Syntax issue on the Main component which is the parent of all. I needed
{React.cloneElement(this.props.children, this.props)}
instead of
{React.cloneElement(props.children, this.props)}
Ref issue

Resources