When I navigate using a react-router <Link> to a page that has a <Switch> component on it with multiple <Route> s, the <Route> that ends up matching is one whose path I was previously on, rather than the current (new) path. It's as if the executes before react-router is aware that the path has changed. This causes the wrong component to render. Am I doing something wrong?
react-router-dom version: 4.2.2
Example:
I have a Header component that looks like this (simplified):
import { Route, Switch } from 'react-router-dom'
export default (props) => {
return (
<Switch>
<Route
path='/checkout'
render={(match, location) => {
console.log('path:', location.pathname)
return <CheckoutHeader>
}}
/>
<Route
path='/cart'
render={(match, location) => {
console.log('path:', location.pathname)
return <NormalHeader>
}}
/>
</Switch>
)
}
On my '/cart' page, I have this:
import { Link } from 'react-router-dom
export default (props) => {
return (
<Link to='/checkout'>Go to checkout</Link>
)
}
If I navigate using the URL bar to my /cart page, I see the Normal header and path: /cart is logged, which is correct.
But when I click the Link, the normal header remains, and path: /cart is logged again. I expect to see path: /checkout here, and the checkout header.
Refreshing the page fixes the problem, and replacing the React-Router link with a regular html link fixes it as well, but then of course we have to re-render the whole page and lose our redux store.
Related
I have a ReactNative app that simply uses a webview to display our react website. Our site allows users to view emails, and in order to properly render these email messages we use an iframe. So our iframe looks like this:
<iframe className={'email-view-body-iframe'} srcDoc={pViewMessage.body}/>
Where srcDoc is the body of the message that is to be viewed, which may be plain text, legacy rtf messages, or html messages. And it works as expected on all platforms except iOS.
I've gone through the webview docs and I'm not seeing anything that jumps out at me, and I know the iframe is there because I styled it with an odd colored background just so I could check that it was indeed being rendered in the correct place.
Can someone point me in the right direction?
For showing static HTML or inline HTML, you need to set originWhiteList to *
import { WebView } from 'react-native-webview';
class MyInlineWeb extends Component {
render() {
return (
<WebView
originWhitelist={['*']}
source={{ html: '<h1>Hello world</h1>' }} />
);
}
}
Similar to the example above you can load an iFrame inside of a WebView.
import { WebView } from 'react-native-webview';
class MyInlineWeb extends Component {
render() {
return (
<WebView
originWhitelist={['*']}
source={{ html: "<iFrame src='your_URL' />" }} />
);
}
}
So as shown in the example above, your iFrame is loaded inside the WebView component. You can then render any web page or URL inside your iFrame then.
Currently developing a ASP MVC site.
Some sections of the site will use VueJS for displaying some list, forms etc.
The project setup is Bower, Grunt, standard C# ASP project using TypeScript.
This is my first time using Vue, and the simple stuff is pretty stragt forward. Seting up a page with a form, getting data from a WebService etc.
My problem/question is, what, and how, do i get the best setup, for using Single File Components (Vue) in my cshtml view files.
So, lets say I have a section on my site, where i want to display orders from the user.
Layout, navigation etc is setup by my excisting ASP code. I have a CSHTML viewpage for the current page, pretty vanilla:
#inherits MyViewPage<MyViewModel>
#{
Layout = "~/Views/layout.cshtml";
}
<div id"app">
</div>
Thats it for the excisting view page. In this page, i want to include a Vue Single File Component.
Previously i had the markup directly in the CSHTML page, which works fine. But when i want to user Vue-router, it becomes a problem to maintain the different views. So i should move the markup into a Component.
This is the basic setup;
const page1 = { template: '<div>Page1</div>' }
const page2 = { template: '<div>Page2</div>' }
const routes = [
{ path: '/', component: page1 },
{ path: '/page2', component: page2 }
]
const router = new VueRouter({
routes
})
var vm = new Vue({
router,
el: "#app"
})
Lets say i create a .vue file called page1.vue instead. This contains
<template>
my new page
</template>
<script>
export default {
name: '?',
date: function() {
}
}
</script>
How do i get this file included in my CSHTML file for instance?
You need to develop with webpack to build Single File Components.
See the Vue documentation on this.
Use the Vue Cli and drop a web pack build into your cshtml page.
Perhaps this is very specific question, but i believe it would be find to find solution. I find a lot of people ask about environment change after login/logout.
I'm using redux to store app state logic about login, modals, forms etc. and relay for data fetching. After login/logout I'm using connected router to trigger rerender with new environment
const RelayApp = connect(_ => ({
routes: routes,
environment: environment,
history: browserHistory,
render: applyRouterMiddleware(useRelay)
}))(Router);
ReactDOM.render(
<Provider store={store}>
<RelayApp />
</Provider>,
document.getElementById('root')
);
I've got my routes defined like this:
export default (renderAppRoute) => (
<Route render={renderAppRoute} path="/" component={App} queries={AppQueries} >
<IndexRoute component={UserList} queries={UserListQueries}/>
<Route path=":username" component={User} queries={UserQueries} />
</Route>
);
renderAppRoute function is defining what to render in different relay states, like if is fetching done
const renderAppRoute = ({ done, props, element }) => {
if(props) { //it's loaded
React.cloneElement(element, props);
}
return <h1>Loading...</h1>;
};
After i change env I'm getting error (index):7 Uncaught TypeError: Cannot read property 'user' of undefined(…). It's because on initial fetch, props is undefined and there's fetching in progress. After fetching is done in props you can find fragment definitions of RelayContainer etc. But after switch to new environment, props is never undefined, so it is rendering my component but without data in store. Is it possible to somehow detect or is there some way to identify if environment is new or do we need to create some custom logic on user side?
I'm currently making it with new variable isNewEnvironment which I set to true, when i create new environment, and false after renderAppRoute has params.done set to true
I'm getting ready to migrate a monolithic rails app to a react app with a rails backend and json api. For now we've integrated the react application as a static asset in our rails app, and are slowly transitioning every page to be rendered by react.
Problem is, that the react app doesn't do the routing (because the rails app handles it currently). Only when all pages have been transferred do we want to transition the frontend completely to react.
However, the react app shouldn't render the same content on every page of course. It should render the appropriate content, based on the page on which it is initiated (and it would reinitiate for every pageload, but the script for the app itself would remain the same).
So the question is, what would be the recommended way to enable react to render the appropriate content. Does it make sense to use react-router, and use it only to render the content based on the url, but not have it handle links?
So in your route file:
export default function(requirePlanData) {
return (
<Route path="/">
<Route path="(:lang/)reading-plans" component={PlansView}>
<Route path=":id(-:slug)" component={AboutPlanView} onEnter={requirePlanData} />
</Route>
)
}
Here you can see that on render of '/en/reading-plans/903' the 'AboutPlanView' component will be rendered, and the 'requirePlanData' function will be called to populate the app state required for the component view.
In your main js file with your requirePlanData you can do call your action creators for your API calls and populate state with your reducers before the view is loaded:
function requirePlanData(nextState, replace, callback) {
const { params } = nextState
var idNum = parseInt(params.id.toString().split("-")[0])
const currentState = store.getState()
if (currentState && currentState.plansDiscovery && currentState.plansDiscovery.plans && currentState.plansDiscovery.plans.id === idNum) {
callback()
} else if (idNum > 0) {
store.dispatch(PlanDiscoveryActionCreators.readingplanInfo({ id: idNum, language_tag: window.__LOCALE__.planLocale }, store.getState().auth.isLoggedIn)).then((event) => {
callback()
}, (error) => {
callback()
})
} else {
callback()
}
}
Now your action creators will return, fire off your reducer to populate state, and your component will be able to render with all the required data–based off of the url/route you defined.
Does this help you?
Ok. I have a url setup to log a user out. On the server, there is no html. The session on the server simply gets destroyed, and then the user is redirected to an address.
This works fine with plain html, but with Angular i am having issues. I've been routing all main routes using $routeProvider.when('/foo', {templateUrl: '/foo.html', controller: 'Ctrl'}) and that works fine for normal templated routes.. however, if there is no template it will not work.
So, how do i support the route /logout in the same fashion as above, when there is no html template?
A workaround is to use template instead of templateUrl. From the Angular docs:
template – {string=} – html template as a string that should be used
by ngView or ngInclude directives. this property takes precedence over
templateUrl.
This can be used as follows:
$routeProvider.when("/foo", {template: " ", controller: "Ctrl"});
Note: You must use " " instead of an empty string "" because Angular uses an if (template) check before firing the controller, and an empty string evaluates to false.
-- EDIT --
A better way to do it is to use the resolve map. See the Angular Docs:
resolve - {Object.=} - An optional map of
dependencies which should be injected into the controller.
This can be used like this:
$routeProvider.when('/foo', {resolve: {redirect: 'RedirectService'}});
Note: I've changed it from "Ctrl" to "RedirectService", because what you're describing in the question isn't really a "controller" in the Angular sense. It doesn't set up scope for a view. Instead, it's more like a service, which ends up redirecting.
I am writing the solution based on the already accepted answer and the github issue mentioned in it's comments.
The approach I am using is a resolve parameter in the $routeProvider. In my case I was trying to create a nice solution to logout in my application, when user goes to /logout.
Example code of $routeProvider:
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
...
when('/logout', {
resolve: {
logout: ['logoutService', function (logoutService) {
logoutService();
}]
},
}).
...
}]);
In the resolve part you specify a service (factory) by name and later on you have to call it. Still it is the nicest solution around.
To make the example complete I present my logoutService:
angular.module('xxx').factory('logoutService', function ($location, Auth) {
return function () {
Auth.setUser(undefined);
$location.path('/');
}
});
Works great!