I am just wondering where I can put Router.events related code in Next.js? Apparently, I can not put it in a specific component, since it needs to be used across the application.
Thank you.
You can put it in a custom App component that is used to init every page.
https://github.com/zeit/next.js#custom-app
Here is some example code that subscribes and unsubscribes to a router event :
const eventHandler = (url) => {...};
export default class MyApp extends App {
constructor(props) {
super(props);
Router.events.on("routeChangeComplete", eventHandler);
}
componentWillUnmount(){
Router.events.off("routeChangeComplete", eventHandler);
}
...
}
HTH
Related
I would need to target different api endpoint roots from different angular modules.
For example
http://server:port/user-api from User module
http://server:port/admin-api from Admin module
http://server:port/checkout-api from Cart module
and so on.
From what I see, the api root called by ngrx/data can be changed but only globally, by means of DefaultDataServiceConfig in app.module
How can I have different api roots in different Angular modules?
At the moment I am using a CustomizeHttpUrlGenerator to change the urls based on a naming convention of mine, but I guess there's a better way.
Thanks
create a provider and change it when you want.
const API_ROOT = new InjectionToken<string>('BASE_HREF', {
factory: () => 'http://server:port/user-api ',
})
then for admin
...
providers: [
{
provide: API_ROOT,
useValue: 'http://server:port/admin-api',
}
],
...
and somewhere in code
constructor(#Inject(API_ROOT) public readonly apiRoot: string) {}
You can add an entry per Entity in the entityResource method of the HttpUrlGenerator.
Hence in the User module for the User entity you would have
#Injectable()
export class UserDataService extends DefaultDataService<User> {
constructor(http: HttpClient, httpUrlGenerator: HttpUrlGenerator) {
httpUrlGenerator.entityResource('User','http://server:port/user-api' );
super('User', http, httpUrlGenerator);
}
}
Similarly to malc answer, I suggest you override the config of the DataService on a case by case scenario.
export class UserDataService extends DefaultDataService<User> {
constructor(http: HttpClient, httpUrlGenerator: HttpUrlGenerator, defaultConfig: DefaultDataServiceConfig) {
super('User', http, httpUrlGenerator, { ...defaultConfig, root: 'http://server:port/user-api' })
}
}
This is less hacky than preemptively setting the knownHttpUrls of the DefaultDataService.
So, I need to redirect to a external page when my site return 400. I've tried follow the Next.js redirect tutorial, didn't work, and I've tried with window.locate, but breaks my site in production.
Here's the code I've tried:
import React from 'react'
import Router from 'next/router'
export default class extends React.Component {
static async getInitialProps({ res }) {
if (res) {
res.writeHead(302, {
Location: 'http://google.com'
})
res.end()
} else {
Router.push('/')
}
return {}
}
}
Is there another solution without the window.locate method?
Thank you!
indeed that is the bad side from next.js the routing is not working well.
i get the same issue before than i change it back to reactjs
kind regards
I have the following component:
export default
#connect(null, dispatch => ({ dispatch }))
class MyComponent extends React.PureComponent {
}
And I have a component wrapping it:
export default class MyWrapper extends React.PureComponent {
comp = React.createRef();
render() {
return <MyComponent ref={this.comp}/>
}
}
How do I access MyComponent using refs?
I tried {withRef: true}, then tried {forwardRef: true} and got all sorts of errors.
Per the React-Redux connect docs, you need to pass {forwardRef : true} as an option to connect:
connect(mapState, mapDispatch, mergeProps, {forwardRef : true}
This is the same whether you're using connect as a separate function, or as a decorator.
On which note: for reference, we do not recommend using connect as a decorator.
The problem is with "react-redux": "7.0.0". The connect function has been implemented as a functional react component, so #decorators is not working anymore.
I am trying to implement alanning Meteor-roles with react-router in my Meteor application. Everything is working fine except the fact I can't manage properly to restrict a route using alanning roles or Meteor.user()
I tried with meteor-roles:
I am trying to use the onEnter={requireVerified} on my route. This is the code:
const requireVerified = (nextState, replace) => {
if (!Roles.userIsInRole(Meteor.userId(), ['verified'],'user_default')) {
replace({
pathname: '/account/verify',
state: { nextPathname: nextState.location.pathname },
});
}
};
I tried with Meteor.user():
const requireVerified = (nextState, replace) => {
if (!Meteor.user().isverified == true) {
replace({
pathname: '/account/verify',
state: { nextPathname: nextState.location.pathname },
});
}
};
So this is working when I am clicking on a route link, but when i manually refresh (F5), it does not work. After digging into it, i have found that Meteor.user() is not ready when i manually refresh the page.
I know Meteor.userid() or Meteor.logginIn() are working, but i wanted
to verify not just that they are logged but if they are "verified" or
have a role.
I also tried to check inside the component with react, with componentDidMount() or componentWillMount(), in both cases it's the same, the manual fresh does not load Meteor.user() before the compenent is mounted.
So what is the best way to restrict components/routes with meteor/alaning roles + react router ? (I am using react-komposer inside TheMeteorChef's base)
Thank you.
Note I have not tried it yet, it's only a suggestion
One thing you could try is to use componentWillReceiveProps alongside createContainer from 'react-meteor-data' like that:
import React, { Component, PropTypes } from 'react';
import { Meteor } from 'meteor/meteor';
import { createContainer } from 'meteor/react-meteor-data';
import { Roles } from 'meteor/alanning:roles';
class MyComponent extends Component {
componentWillReceiveProps(nextProps) {
const { user } = nextProps;
if (user && !Roles.userIsInRole(user._id, ['verified'], 'user_default')) {
browserHistory.push('/account/verify');
}
// If Meteor.user() is not ready, this will be skipped.
}
}
MyComponent.propTypes = {
user: PropTypes.object,
};
export default createContainer(() => {
const user = Meteor.user() || null;
return { user };
}, MyComponent);
To explain the flow, when the page is loaded, as you said Meteor.user() is not defined so you can't check the permissions. However, when Meteor.user() gets defined, this will trigger a refresh of the template, and the new props will be passed to componentWillReceiveProps. At this moment you can check if user has been defined and redirect if needed.
To be really sure not to miss anything, I would actually put the verification in the constructor() as well (defining a function that takes the props as arguments and calling it in both constructor() and componentWillReceiveProps()).
I have the following simplified Component for a dashboard. The dashboard object is injected via props. The handleDeleteDashboard action checks if the dashboard isn't the last available one. If it is, you are not allowed to delete it. For this check I need nrOfDashboards which I get from the store in mapStateToProps. So I connected the Component to the redux store.
class Dashboard extends Component {
constructor(props) {
super(props);
this.handleDeleteDashboard = this.handleDeleteDashboard.bind(this);
}
handleDeleteDashboard() {
const { dashboardDeleteAction, dashboard, nrOfDashboards } = this.props;
if (nrOfDashboards < 2) {
// NOT Allowed to delete
} else {
dashboardDeleteAction(dashboard.id);
}
}
render() {
const { dashboard } = this.props;
return (
<Content>
<h1>{dashboard.name}</h1>
<Button onButtonClick={this.handleDeleteDashboard}>Delete</Button>
</Content>
);
}
}
Dashboard.propTypes = {
dashboard: customPropTypes.dashboard.isRequired,
nrOfDashboards: PropTypes.number.isRequired
};
function mapStateToProps(state) {
return {
nrOfDashboards: selectNrOfDashboards(state)
}
}
export default connect(mapStateToProps, { dashboardDeleteAction: dashboardActionCreators.dashboardDelete })(Dashboard);
But now the Component is subscribed to the store and updates whenever nrOfDashboards changes (I know I can perform a shouldComponentUpdate here to prevent if from re-rendering but that is not the point). So I am basically subscribing to changes on nrOfDashboards although I only need this information when I actively click on the delete button.
So I came up with a alternative solution where I disconnected the Component from the store and access the store via context in the handleDeleteDashboard method.
class Dashboard extends Component {
constructor(props) {
...
}
handleDeleteDashboard() {
const { dashboardDeleteAction, dashboard } = this.props;
const store = this.context;
if (selectNrOfDashboards(store.getState()) < 2) {
// NOT Allowed to delete
} else {
dashboardDeleteAction(dashboard.id);
}
}
render() {
...
}
}
Dashboard.propTypes = {
dashboard: customPropTypes.dashboard.isRequired,
};
Dashboard.contextTypes = {
store: PropTypes.object
};
export default connect(null, { dashboardDeleteAction: dashboardActionCreators.dashboardDelete })(Dashboard);
This works fine for me and whenever I actively click the button I ensure to get the fresh state from the store. Anyhow, I have not seen this technique somewhere else before and also read somewhere that accessing the store should not be done outside of mapStateToProps. But my question is if direct access to the store on demand is an anti-pattern and if I better should follow code example one, where I connect the Component to the store?
Yes. Direct access to the store is considered an anti-pattern. Idiomatic Redux code uses basic dependency injection - connect() and its mapState() and mapDispatch() arguments give you the data your component needs and the reference to dispatch, and middleware like Redux-Thunk gives you access to getState() and dispatch() in your action creators.
Ideally, your component would simply dispatch an action creator, and let the action creator logic worry about whether or not to really dispatch a real action. So, in your case, that might look like:
// action creator
export function deleteDashboard(dashboardID) {
return (dispatch, getState) => {
const state = getState();
const numberOfDashboards = selectNumberOfDashboards(state);
if(numberOfDashboards >= 2) {
dispatch({
type : "DELETE_DASHBOARD",
payload : {
dashboardID
}
});
}
}
}
// component
handleDeleteDashboard() {
const {dashboard} = this.props;
this.props.dispatch(deleteDashboard(dashboard.id));
}
See the Redux FAQ question on this topic: http://redux.js.org/docs/FAQ.html#store-setup-multiple-stores