right now migrating from router v2 to v3 (feels like a deja vu). The routing configuration is now decoupled from the components again. It overthrows the logic I considered quite sensible. They have introduced a children property in the RouterConfig which gives me headache. Assume an application with many routes similar to this
/club/123/member/98/tasklist/921/edit
The route was spread over the following components with the following #Routes decorators
#Routes([{path: '/club/:id', component: DelegateClubComponent}])
export class MainApp {...}
#Routes([{path: 'user/:id', component: DelegateUserComponent}])
export class DelegateClubComponent {...}
#Routes([{path: 'tasklist/:id', component: DelegateTaskListComponent}])
export class DelegateUserComponent {...}
#Routes([{path: 'edit', component: EditTaskListComponent}])
export class DelegateTaskListComponent {...}
Each of the DelegateXComponents were responsible for resolving the respective document and making it available in a Service the other components get injected. Moreoever, all of the DelegateXComponents rendered a little template with some data of the documents they were in charge of.
How is this done with router v3 ? It makes no sense to map the entire tree in a nested RouterConfig with 5 levels of children. On the other hand, do separate RouterConfigs work at all?
export const clubRoute: RouterConfig = [
{ path: 'club/:id', component: DelegateClubComponent }];
export const userRoute: RouterConfig = [
{ path: 'user/:id', component: DelegateUserComponent }];
As long as there is no magic happening behind the scenes, how would the router know that the userRoute should be considered as a child route for clubRoute.
Confused greetings
You can define configs in the same files as the components and then just combine them to a complete tree before passing it to the router.
import {childRoutes} from './child.component'
export const clubRoute: RouterConfig = [
{ path: 'club/:id', component: DelegateClubComponent, children: childRoutes }];
Related
I've begun working on a rather large react project in which a large upgrade to the libraries is required.
Most migrations for non-supported libraries has gone smooth, but the combination of react-redux and react-router v4-5 is could cause a bit of issue, due to the initial implementation of plain routes (in combination with a fractal design pattern)
In the meantime, I have migrated to react-router v3.2.3 as it handles the newer version of react-redux. However, this is just moving the goal post.
Here is an example of the nested routes
export const createRoutes = (store) => ({
path : '/',
indexRoute : { component: AuthLoginContainer },
childRoutes : [
DashboardRoute(store),
SettingsRoute(store)
]
})
// DashboardRoute index.js
export default (store) => ({
path : 'dashboard',
indexRoute: { component: DashboardContainer },
component: isAuthContainer(BaseLayout),
childRoutes: [ ProfileRoute(store) ]
})
// ProfileRoute index.js
export default (store) => ({
path : 'profile',
indexRoute: { component: ProfileContainer },
})
The example on https://reactrouter.com/web/example/route-config seems to work somewhat, but I'm unsure how to implement a similar pattern with the newer version of react-router
Any tips on how to even begin to tackle this (if possible) or how similar patterns are implemented in react-router5 would be appreciated.
I'm hoping it's not a full rewrite as the fractal design with these routes is pretty baked into the application
I'm trying to make a React route path dynamic and for this I need to send a variable clickedSubprojectName to my routes.js.
right now my routes.js looks like this:
import React from 'react';
const Projects = React.lazy(() => import('./views/Projects'));
const Subproject= React.lazy(() => import('./views/Subproject'));
const routes = [
{ path: '/projects', exact: true, name: 'Projects', component: Projects },
{ path: "/projects/subprojects", name: 'Subproject', component: Subproject},
];
export default routes;
In my Projects.js I created a state variable which saves the name of the subproject where the user clicked on. And I want to pass this variable from my Projects class to my routes.js so that I can set the path in routes for example like this:
{ path: "/projects/"+clickedSubprojectName, name: 'Subproject', component: Subproject}
I've already tried to export a constant variable from my Projects.js like this:
export const clickedSubprojectName={
clickedSubprojectName: this.state.clickedSubprojectName}
and then imported import { clickedSubprojectName} from './views/Subproject' in my routes.js
but this isn't really working. This sets the clickedSubprojectName as the default of the state variable at the first time when the Projects component gets rendered and it doesn't get updated when the state variable changes.
Hope anyone have a solution for this problem because I haven't found anything on stackoverflow yet
Thanks
for dynamic routes.. you can use something like..
{ path: "/projects/:subproject", name: 'Subproject', component: Subproject},
and you can invoke this route by history.push("/projects" + this.state.clickedSubprojectName)
export const clickedSubprojectName={
clickedSubprojectName: this.state.clickedSubprojectName}
this definitely won't work.. this variable gets exported once and imported once. So it will always return the default value.
We are building a Storybook UI library from our existing code base. The code wasn't written with component driven development in mind. There are many instances where a component renders descendants that are connected to the Redux store.
E.g., Parent (connected) -> Child (unconnected) -> Grandchild (connected)
Now if I'm building a story for Parent, I understand how to pass hard-coded data as a prop to an immediate child component in order to avoid Redux all together. However, I can't figure out how to do this when the connected component is more deeply nested.
Ideally I don't want to have to use Redux at all for stories, but even if I do initialize a Redux store and wrap the parent component in a Provider as described here, would this even work to connect the grandchild component?
Any ideas would be helpful.
When using storybook you can add a Decorator for all stories (see link for most updated API).
It is common to wrap your stories with the state manager store provider in order to not break the story avoiding "adding a store for each story".
// # config.js
import { configure, addDecorator } from '#storybook/react';
import React from 'react';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import rootReducer from 'reducers/root.reducer';
const store = createStore(rootReducer);
addDecorator(S => (
<Provider store={store}>
<S />
</Provider>
));
configure(require.context('../src', true, /\.stories\.js$/), module);
Note that you can avoid connecting all your components with redux-hooks which in addition removes all the boilerplate code of redux.
React Redux now offers a set of hook APIs as an alternative to the existing connect() Higher Order Component. These APIs allow you to subscribe to the Redux store and dispatch actions, without having to wrap your components in connect().
If you want to solve the problem within your story file (and just fetch your store), use decorator like this:
import React from "react";
import { Provider } from 'react-redux'
import Parent from "./Parent";
import { store } from "../../../redux/store";
export default = {
title: "pages/Parent",
component: Parent,
decorators : [
(Story) => (<Provider store={store}><Story/></Provider>)
]
};
Sidenote, if this gives you the error useNavigate() may be used only in the context of a <Router> component., then you may need <MemoryRouter><Provider store={store}><Story/></Provider></MemoryRouter> (import {MemoryRouter} from 'react-router-dom')
I am trying to use a CanActivate guard in angular2 to ask user to save changes.
I am using angular RC4
Boot.ts
bootstrap(App, [
...HTTP_PROVIDERS,
StorageService,
PortfolioNavigationGuard,
other...
Routes.ts
export const PortfolioRoutes: RouterConfig = [
{ path: 'portfolios', component: 'PortfolioListComponent' },
{ path: 'portfolios/:id', component: 'PortfolioModelComponent', canDeactivate: [PortfolioNavigationGuard] },
{ path: 'portfolios/:id/:action', component: 'PortfolioModelComponent', canDeactivate: [PortfolioNavigationGuard] }
Guard
#Injectable()
export class PortfolioNavigationGuard implements CanDeactivate<PortfolioDetailComponent> {
constructor(private router: Router) { }
canDeactivate(component: PortfolioDetailComponent) {
component.canDeactivate();
component.anyfunction() --- anyfunction is not a function
return true;
}
The error
EXCEPTION: Error: Uncaught (in promise): TypeError: component.canDeactivate is not a function
The component
export class PortfolioDetailComponent {
canDeactivate() {
console.log("WOW");
}
Why can't I call any functions on the component inside my guard?
I have made some changes:
Switched the guard implementation to a different component ( same one I defined in the route config) :
export class PortfolioNavigationGuard implements CanDeactivate<PortfolioDetailComponent> {
to
export class PortfolioNavigationGuard implements CanDeactivate<PortfolioModelComponent> {
There are two things to note. The first component is not loaded through router outlet, while the second is. I was thinking that components that belong to another(used as tag in template) still can access the route data (You can subscribe to the route id change let's say in components that were brought using a selector and not a router outlet), however it seems that for the guard, the component that you are putting the guard on must be brought with router outlet.
I have a redux reducer loaded with several reactjs components.
I want to load these inside other components through this.props
Like: this.props.components.MyReactComponent
class OtherComponent extends Component {
render() {
const Component = this.props.components.MyReactComponent
return (
<div>
<Component />
</div>
)
}
}
Is this possible? If so, how?
EDIT The component is a connected component. I am able to load it but it is broken. In this case, it is a counter, when you click to increment or decrement nothing happens. In the console, there is this error:
Uncaught ReferenceError: _classCallCheck is not defined
if I convert the component into a dumb component (without connecting it), the error is this:
Uncaught ReferenceError: _classCallCheck3 is not defined
EDIT 2
I found out why those errors show up. It is because the react component gets stripped out when stored in the reducer:
A react component would look something like this:
{ function:
{ [Function: Connect]
displayName: 'Connect(Counter)',
WrappedComponent: { [Function: Counter] propTypes: [Object] },
contextTypes: { store: [Object] },
propTypes: { store: [Object] } } }
However, after I store it inside a reducer, it loses its properties and ends up looking something like this:
{ function:
{ [Function: Connect] } }
After reading the comments below, I thought of an alternative. I can store in a reducer the path to each component, then make a new wrapper component that could render those other components from those paths.
I tried it but encoutered a different problem with the funcion require from nodejs that for some weird reason is not letting me user a variable as an argument. For example:
This works:
var SomeContent = require('../extensions/myContent/containers')
This does not:
var testpath = '../extensions/myContent/containers'
var SomeContent = require(testpath)
Giving me the following error:
Uncaught Error: Cannot find module '../extensions/myContent/containers'.
It is adding a period at the end of the path. How can I prevent require to add that period?
If you can think of any other alternative I can implement for what I am trying to do, I would greatly appreciate it.
EDIT 3 Following Thomas advice...
What I am trying to accomplish is this:
I want to be able to render react components inside other react components, I know how to do it the same way most us know how to; however, I want to be able to do it by importing a file that would contain all the components without actually having to import and export each one of them:
OtherComponent.js
import React, { Component } from 'react'
import { SomeComponent } from '../allComponentes/index.js'
export default class OtherComponent extends Component {
render() {
return (
<SomeComponent />
)
}
}
SomeComponent.js
import React, { Component } from 'react'
export default class SomeComponent extends Component {
render() {
return (
<div>
Hello
</div>
)
}
}
allComponents/index.js
import SomeComponent from '../allComponents/SomeComponent/index.js'
export { SomeComponent }
What I am trying to do in allComponents/index.js is to avoid having import/export statements for each component by reading (with fs module) all the components inside the allComponents folder and export them.
allComponents/index.js (pseudocode)
get all folders inside allComponents folder
loop through each folder and require the components
store each component inside an object
export object
When I tried that, I encountered multiple issues, for one, export statements have to be in the top-level, and second, fs would work only on the server side.
So, that is why I thought of loading all the components in a reducer and then pass them as props. But as I found out, they got stripped out when stored them in a reducer.
Then, I thought of only storing the path to those components inside a reducer and have a wrapper component that would use that path to require the needed component. This method almost worked out but the nodejs function require wont allow me to pass a variable as an argument (as shown in EDIT 2)
I think your question is not really to do with redux but rather is (as you say):
What I am trying to do in allComponents/index.js is to avoid having import/export statements for each component by reading (with fs module) all the components inside the allComponents folder and export them.
By way of example, I have all of my (dumb) form components in a folder path components/form-components and the index.js looks something like:
export FieldSet from './FieldSet'
export Input from './Input'
export Label from './Label'
export Submit from './Submit'
export Select from './Select'
export Textarea from './Textarea'
Then when I want to import a component elsewhere, it is import { FieldSet, Label, Input, Submit } from '../../components/form-components/';