React context inside Route - javascript

I am trying to get context variables inside a React Route. This is my code
<Switch>
<Route exact path="/" component={Home} />
<Route path="/home/:loc" render={(props)=> {
<AppContext.Consumer>
{
context=><Home context={context} />
}
</AppContext.Consumer>
}} />
</Switch>
This is my AppContext.js
import React from 'react';
export const AppContext = React.createContext();
This is how I am setting the context
<AppContext.Provider value={this.state}>
<div className="App">
<Header />
<Content />
<Footer />
</div>
</AppContext.Provider>
This is the error I am getting
Expected an assignment or function call and instead saw an expression no-unused-expressions
Any idea whats wrong here?

Expected an assignment or function call and instead saw an expression no-unused-expressions
is not an error but linter warning. no-unused-expressions rule often indicates that the code may not work as intended because of developer's mistake.
The problem with posted code is that render arrow function doesn't return anything.
It should be:
<Route path="/home/:loc" render={(props)=> (
<AppContext.Consumer>
{
context=><Home context={context} />
}
</AppContext.Consumer>
)} />
=> {...} requires explicit return, while => (...) is implicit return.

Related

TypeScript - ReactRouter | Arrow function captures the global value of 'this' which implicitly has type 'any'

I'm rendering a component via React Router 4 using render={() => </Component />}
I need to pass state to the given component i.e: <Game />
export const Routes: React.SFC<{}> = () => (
<Switch>
<Route path="/" exact={true} component={Home} />
<Route path="/play-game" render={() => <Game {...this.state} />} />
<Redirect to="/" />
</Switch>
)
To which TS breaks saying:
The containing arrow function captures the global value of 'this' which implicitly has type 'any'
The final goal is to be able to pass the Routes to my main app: i.e:
export default class App extends Component<{}, AppState> {
public state = {
// state logic
}
public render(): JSX.Element {
return (
<BrowserRouter>
<div className="App">
<Navigation />
<Routes />
</div>
</BrowserRouter>
)
}
}
How could I apply the correct types to suppress this TypeScript error?
Arrow functions do not have lexical contexts, so any invocation of this inside the body of an arrow will degenerate to its value in the outer scope. This is what TS is complaining about.
For your problem of passing the state around you need to pass this as a prop to the Routes component which will dispatch it to the relevant route.
export default class App extends Component<{}, AppState> {
public state = {
// state logic
}
public render(): JSX.Element {
return (
<BrowserRouter>
<div className="App">
<Navigation />
<Routes state={this.state}/>
</div>
</BrowserRouter>
)
}
}
// you need to pass the correct type to React.SFC<>
// probably something along React.SFC<{ state: State }>
// where State is the type of `state` field in App.
export const Routes: React.SFC<...> = ({ state }) => (
<Switch>
<Route path="/" exact={true} component={Home} />
<Route path="/play-game" render={() => <Game {...state} />} />
<Redirect to="/" />
</Switch>
)

module.default is undefined dynamic import

I'm trying to use React.lazy to lazy load a React Component.
The component looks like this:
function App() {
const HubScreen = React.lazy(() => import("./screens/hub").then((mod) => {
console.log(mod.default)
return mod.default;
}));
return (
<BrowserRouter>
<MuiThemeProvider theme={theme}>
<MainContaier>
<div id="screen">
<CssBaseline />
<Switch>
<React.Suspense fallback={<h1>Loading...</h1>}>
<Route exact path="/" component={HomeScreen} />
<Route path="/hub" render={() => <HubScreen />} />
</React.Suspense>
</Switch>
</div>
</MainContaier>
</MuiThemeProvider>
</BrowserRouter >
)
}
And this is the component I'm importing
import React from "react";
function HubScreen() {
return (
<div>Hi</div>
);
}
export default HubScreen;
When I navigate to /hub I see the value of mod.default as undefined. Along with my Chrome window becoming completely unresponsive, requiring a force stop.
I know that my path to the module ./screens/hub, is correct because, if I put a fake path like ./screens/hube then webpack gives me the error:
Module not found: Error: Can't resolve './screens/hube' in '/home/travis/Workspace/avalon/assets/js'
I'm stumped haven't found a similar problem anywhere.
This answer gave me some insight as to why my browser was hanging up. However I still seem to have the same root problem; the undefined module.default. After changing the root component to this:
const HubScreen = React.lazy(() => import("./screens/hub"));
function App() {
return (
<BrowserRouter>
<MuiThemeProvider theme={theme}>
<MainContaier>
<div id="screen">
<CssBaseline />
<Switch>
<React.Suspense fallback={<h1>Loading...</h1>}>
<Route exact path="/" component={HomeScreen} />
<Route path="/hub" component={HubScreen} />
</React.Suspense>
</Switch>
</div>
</MainContaier>
</MuiThemeProvider>
</BrowserRouter >
)
}
I get the War:
Warning: lazy: Expected the result of a dynamic import() call. Instead received: [object Object]
Your code should look like:
const MyComponent = lazy(() => import('./MyComponent'))
And then the error:
Uncaught Error: Element type is invalid. Received a promise that resolves to: undefined. Promise elements must resolve to a class or function.
Which I have taken to mean that undefined is being returned from the import resolution, as the console.log seems to confirm.
Also another possibility of getting this error is by missing the default export in your Component which becomes a named export.
This syntax is only supported for Default exports.
const HubScreen = React.lazy(() => import("./screens/hub"));
import React, {useState} from 'react';
const ChangeName = (props) => {
return (
<div>
<p>Name: {props.name}</p>
<p>Email: {props.email}</p>
<div>
<button onClick={()=>props.changeStatus()}>Change Details</button>
</div>
</div>
)
}
export default ChangeName; ====> By missing the default Export
First off, move your const HubScreen outside of your component. When App() rerenders, it will cause an infinate loop to keep trying to load HubScreen over and over again. Secondly, just use () => import... and let React.lazy use the default component exported. Additionally, you should not need to use render for the route. Just provide the component:
const HubScreen = React.lazy(() => import("./screens/hub"));
function App() {
return (
<BrowserRouter>
<MuiThemeProvider theme={theme}>
<MainContaier>
<div id="screen">
<CssBaseline />
<Switch>
<React.Suspense fallback={<h1>Loading...</h1>}>
<Route exact path="/" component={HomeScreen} />
<Route path="/hub" component={HubScreen} />
</React.Suspense>
</Switch>
</div>
</MainContaier>
</MuiThemeProvider>
</BrowserRouter >
)
}

Failed prop type: Invalid prop `component` of type `object` supplied to `Route`, expected `function`

I have just updated my React app to 16.6.0 and react-scripts to 2.0.3 to start using lazy and I got this error when following an example on official docs:
Failed prop type: Invalid prop component of type object supplied to Route, expected function
Disregarding of it everything seems to be working, except this error in the console.
Here is some of my code:
// imports here
...
const Decks = lazy(() => import('./pages/Decks'));
...
class App extends Component {
...
render() {
return (
<ConnectedRouter history={history}>
<div>
<MenuAppBar />
<div style={{paddingTop: '4rem'}}>
<Suspense fallback={<LazyLoading />}>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/decks" component={Decks} />
...
</Switch>
</Suspense>
</div>
<Footer />
</div>
</ConnectedRouter>
);
}
...
}
What could I be doing wrong here?
When using a lazy loaded component, you would need to supply it to the Route component like
// imports here
...
const Decks = React.lazy(() => import('./pages/Decks'));
...
class App extends Component {
...
render() {
return (
<ConnectedRouter history={history}>
<div>
<MenuAppBar />
<div style={{paddingTop: '4rem'}}>
<Suspense fallback={<LazyLoading />}>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/decks" render={(props) => <Decks {...props} />} />
...
</Switch>
</Suspense>
</div>
<Footer />
</div>
</ConnectedRouter>
);
}
...
}
Probably its an incorrect PropType check in react-router and may have been fixed in the latest versions to make it compatible with react v16.6
Update "react-router-dom" to "^4.4.0-beta.6" cat fix it.
It is a bug: https://github.com/ReactTraining/react-router/issues/6420#issuecomment-435171740

Why is this.props undefined in conditional routing

I am trying to create a conditional route by creating a ProtectedRoute component as described by the chosen answer of this question.
The condition comes from the props passed into the ProtectedRoute component. Please have a look at the component and routing code below.
import React, {Component} from "react";
import { Route } from 'react-router-dom';
import { Redirect } from 'react-router';
class ProtectedRoute extends Component {
render() {
const { component: Component, ...props } = this.props
return (
<Route
{...props}
render={props => (
this.props.profile.name === "admin" ?
<Component {...props} /> :
<Redirect to='/login' />
)}
/>
)
}
}
export default ProtectedRoute;
The following is how I achieve the routing in a separate side navigation bar component. The profile object is passed as props to this component from App.js.
<main>
<Route path="/" exact component={props => <Home/>} />
<ProtectedRoute path="/dashboard" component={props => <Dashboard profile={this.props.profile} />} />
</main>
The error I am getting when running the above application is: TypeError: _this2.props.pofile is undefined. However, when I put a Route instead of ProtectedRoute i.e.
<Route path="/dashboard" component={props => <Dashboard profile={this.props.profile} />} />,
the application works as expected.
Could someone please assist me by pointing out what I am doing wrong? That would be much appreciated.
Inside Route's render property you use an arrow function which means that context inside it is bound to ProtectedRoute's instance. this.props inside render resolve to props of ProtectedRoute in other words. To solve this issue you need to pass profile to ProtectedRoute instead of Dashboard:
<main>
<Route path="/" exact component={props => <Home/>} />
<ProtectedRoute path="/dashboard" profile={this.props.profile} component={props => <Dashboard />} />
</main>
The reason why _this2.props.pofile is undefined - you haven't passed it to ProtectedRoute component, however, you passed it to Dashboard.
The right way to pass it is:
<ProtectedRoute path="/dashboard" profile={this.props.profile} component={props => <Dashboard profile={this.props.profile} />} />
By the way, it is not the best practice to pass JSX as a prop, better to pass it as children:
<ProtectedRoute path="/dashboard" profile={this.props.profile}>
<Dashboard profile={this.props.profile} />
</ProtectedRoute>
and then inside of ProtectedRoute just render {this.props.children}.
On this error TypeError: _this2.props.pofile is undefined it's pofile and not profile
Some where you define the wrong typo maybe.

Nested Routing on exported components with React Router

I was following a CRUD tutorial that used nested Routes like the code below. I tried to omit most of the code that doesn't concern routing.
After looking up several other tutorials on nested routing I noticed they don't use exported components like I was. I also noticed that the tutorial code below exported its components using withRouter.
index.js:
...imports
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root'),
);
App.js:
const App = ({ classes }) => (
<CssBaseline />
<AppHeader />
<main className={classes.main}>
<Home />
<Route exact path="/" component={Home} />
<Route exact path="/posts" component={PostManager} /> //This renders a list of posts
</main>
</Fragment>
PostsManager.js:
...imports
...constructor and other functions (this.savePost)
renderPostEditor = ({ match: { params: { id } } }) => {
if (this.state.loading) return null;
const post = find(this.state.posts, { id: Number(id) });
if (!post && id !== 'new') return <Redirect to="/posts" />;
return <PostEditor post={post} onSave={this.savePost} />;
};
render() { //component render funciton
...
<Button
variant="fab"
color="secondary"
aria-label="add"
className={classes.fab}
component={Link}
to="/posts/new"
>
<Route exact path="/posts/:id" render={this.renderPostEditor} />
}
...exporting component withRouter()
The problem I got was that when I tried to access /posts/new or /posts/2 which both should match /posts/:id, I didn't get any match. The method this.renderPostEditor obviously didn't get called and PostEditor wasn't rendered.
I tried to solve the problem by removing the Route in PostsManager.js and putting in App.js. That way I got a match but it didn't render the way I wanted because this.renderPostEditor dependended on PostManager.js
My question is why I didn't get a match inside PostsManager.js but got match in App.js?
Try removing the exact prop from <Route ... /> definition.

Categories

Resources