'react-router-dom' does not contain an export named 'useParams' - javascript

In App.js I have the following route:
<Route path="/Comp1/:catid">
<Comp1 />
</Route>
This route can be called by clicking this link with a single parameter:
<Link to={'/Comp1/' + catid}>Comp1 </Link>
The parameter catid always has a certain value.
Comp1 is a component defined as follows:
import React, { Component } from 'react';
import { useParams } from 'react-router-dom';
export default class Comp1 extends Component {
componentDidMount() {
const params = useParams();
const { catid } = params.catid;
console.log(catid);
}
componentDidUpdate() {
}
render() {
return (
<div>
Hello, I am class Comp1
</div>
);
}
};
But what is happening now is at runtime, I am getting the following debug output:
'react-router-dom' does not contain an export named 'useParams'
--edit--
Installed version of react-router-dom is 4.2.2.

Definitely a version issue. Also discussed in this official issue.
Solution: Make sure you are using react-router-dom#>=5.1, since that particular hook was introduced in the 5.1 release.
If you have trouble getting modules installed at a proper version, then is the perfect time to practice just that, especially since it is very likely going to happen again and again, if you don't.

Related

React JS, props validation

New to React JS, props validation practise as follows:
import React from 'react';
import PropTypes from 'prop-types';
class App extends React.Component {
render() {
return (
<div>
<h3>String : {this.props.propstring}</h3>
</div>
);
}
}
App.propTypes = {
propstring : PropTypes.string
}
App.defaultProps = {
propstring : "My Name"
}
export default App;
import React, { component } from 'react';
import ReactDOM from 'react-dom';
import App from './AppProp.js';
ReactDOM.render(<App />,document.getElementById('root'));
Getting an error as:
TypeError: Class extends value undefined is not a constructor or null.
What am I missing here and how can I resolve the above error?
There are 2 possible problems. Most probably you're importing { component }(like you do in your index file, which is wrong), instead of { Component } somewhere in your code and trying to extend component which doesn't exist.
The other reason is that you might be circularly importing classes, but I doubt it. Also, is your file called AppProp.js?
Post the stack trace and it would also help if you post all the components you're using. Post your package.json as well, as you might be using wrong absolute paths.
The code is perfectly fine. I executed it on my local machine and it ran fine.
The only issue you may be facing is to with
circularly importing classes, which is apparently a limitation
Know more from the resource:
https://esdiscuss.org/topic/how-to-solve-this-basic-es6-module-circular-dependency-problem

Code works on react-router-dom 4.3.1 but not on 5.2.0

tl;dr: code below does not work on 5.2.0 version of react-router-dom but works on 4.3.1
Context: I was tinkering around with some client-side-routing code online using CodeSandBox specifically utilizing the react-router-dom module. Below is some relevant snippets of the App:
index.js
import React from "react";
import ReactDOM from "react-dom";
import { Link, BrowserRouter } from "react-router-dom";
import StyledMenuItem from "./StyledMenuItem";
function App() {
return (
<React.Fragment>
<StyledMenuItem component={Link} to={"/hello"}>
<span>Hello Link</span>
</StyledMenuItem>
<br />
<br />
<Link to={"/world"}>World Link</Link>
</React.Fragment>
);
}
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.querySelector("#app")
);
StyledMenuItem.jsx
import React, { Component } from "react";
export default class StyledMenuItem extends Component {
render() {
let WrapperComponent = this.props.component;
if (WrapperComponent === undefined) {
WrapperComponent = React.Fragment;
}
return (
<WrapperComponent {...this.props}>{this.props.children}</WrapperComponent>
);
}
}
Basically, I was tinkering around & trying to imitate the behavior of the component prop of the MenuItem API of the Material-UI.
The issue that had me puzzled: The Link that goes to '/hello' did not work on CodeSandBox but it worked on my local environment. I am using the same exact code with the same versions of react & react-dom which is 16.13.1.
Upon my investigation: I am not updated on my react-router-dom on my local environment. On the CodeSandBox, I was using react-router-dom 5.2.0; on my local environment I am using react-router-dom 4.3.1
So for that, back on CodeSandBox I tried testing it on the different versions (CodeSandBox has a cool feature that enables you to change package versions).
I've tested it on both versions & I can confirm that this issue definitely has something to do with my package versions.
Now, my question: what was the code change on the react-router-dom 5.2.0 package that stopped supporting my code which is working on react-router-dom 4.3.1?
You could easily compare the source code, but with just a quick glance at your code my guess it is something to do with the extra props you also pass along to the Link. If you destructure component out of the props then it appears to work in your sandbox.
import React, { Component } from "react";
export default class StyledMenuItem extends Component {
render() {
const { component, ...rest } = this.props; // <-- destructure and pass on the rest
let WrapperComponent = component;
if (WrapperComponent === undefined) {
WrapperComponent = React.Fragment;
}
return <WrapperComponent {...rest}>{this.props.children}</WrapperComponent>;
}
}

React-router v4 this.props.history.push(...) not working

I'm trying to route programatically using this.props.history.push(..) but it doesn't seem to work.
Here's the router:
import {
BrowserRouter as Router,
Route
} from 'react-router-dom';
<Router>
<Route path="/customers/" exact component={CustomersList} />
<Route path="/customers/:id" exact component="{Customer} />
</Router>
In CustomerList, a list of customers is rendered. Clicking on a customer (li) should make the application route to Customer:
import { withRouter } from 'react-router'
class Customers extends Component {
static propTypes = {
history: PropTypes.object.isRequired
}
handleCustomerClick(customer) {
this.props.history.push(`/customers/${customer.id}`);
}
render() {
return(
<ul>
{ this.props.customers.map((c) =>
<li onClick={() => this.handleCustomerClick(c)} key={c.id}>
{c.name}
</li>
</ul>
)
}
}
//connect to redux to get customers
CustomersList = withRouter(CustomersList);
export default CustomersList;
The code is partial but illustrates perfectly the situation.
What happens is that the browser's address bar changes accordingly to history.push(..), but the view does not update, Customer component is not rendered and CustomersList is still there. Any ideas?
So I came to this question hoping for an answer but to no avail. I have used
const { history } = this.props;
history.push("/thePath")
In the same project and it worked as expected.
Upon further experimentation and some comparing and contrasting, I realized that this code will not run if it is called within the nested component. Therefore only the rendered page component can call this function for it to work properly.
Find Working Sandbox here
history: v4.7.2
react: v16.0.0
react-dom: v16.0.0
react-router-dom:
v4.2.2
It seems things have changed around a bit in the latest version of react router. You can now access history via the context. this.context.history.push('/path')
Also see the replies to the this github issue: https://github.com/ReactTraining/react-router/issues/4059
You can try to load the child component with history. to do so, pass 'history' through props. Something like that:
return (
<div>
<Login history={this.props.history} />
<br/>
<Register/>
</div>
)
For me (react-router v4, react v16) the problem was that I had the navigation component all right:
import { Link, withRouter } from 'react-router-dom'
class MainMenu extends Component {
render() {
return (
...
<NavLink to="/contact">Contact</NavLink>
...
);
}
}
export default withRouter(MainMenu);
Both using either
to="/contact"
or
OnClick={() => this.props.history.push('/contact')};
The behavior was still the same - the URL in browser changed but wrong components were rendered, the router was called with the same old URL.
The culprit was in the router definition. I had to move the MainMenu component as a child of the Router component!
// wrong placement of the component that calls the router
<MainMenu history={this.props.history} />
<Router>
<div>
// this is the right place for the component!
<MainMenu history={this.props.history} />
<Route path="/" exact component={MainPage} />
<Route path="/contact/" component={MainPage} />
</div>
</Router>
You can get access to the history object's properties and the closest 's match via the withRouter higher-order component. withRouter will pass updated match, location, and history props to the wrapped component whenever it renders.
import React, { Component } from 'react'
import { withRouter } from 'react-router';
// you can also import "withRouter" from 'react-router-dom';
class Example extends Component {
render() {
const { match, location, history } = this.props
return (
<div>
<div>You are now at {location.pathname}</div>
<button onClick={() => history.push('/')}>{'Home'}</button>
</div>
)
}
}
export default withRouter(Example)
Seems like an old question but still relevant.
I think it is a blocked update issue.
The main problem is the new URL (route) is supposed to be rendered by the same component(Costumers) as you are currently in (current URL).
So solution is rather simple, make the window url as a prop, so react has a chance to detect the prop change (therefore the url change), and act accordingly.
A nice usecase described in the official react blog called Recommendation: Fully uncontrolled component with a key.
So the solution is to change from
render() {
return(
<ul>
to
render() {
return(
<ul key={this.props.location.pathname}>
So whenever the location changed by react-router, the component got scrapped (by react) and a new one gets initiated with the right values (by react).
Oh, and pass the location as prop to the component(Costumers) where the redirect will happen if it is not passed already.
Hope it helps someone.
I had similar symptoms, but my problem was that I was nesting BrowserRouter
Do not nest BrowserRouter, because the history object will refer to the nearest BrowserRouter parent. So when you do a history.push(targeturl) and that targeturl it's not in that particular BrowserRouter it won't match any of it's route, so it will not load any sub-component.
Solution
Nest the Switch without wrapping it with a BrowserRouter
Example
Let's consider this App.js file
<BrowserRouter>
<Switch>
<Route exact path="/nestedrouter" component={NestedRouter} />
<Route exact path="/target" component={Target} />
</Switch>
</BrowserRouter>
Instead of doing this in the NestedRouter.js file
<BrowserRouter>
<Switch>
<Route exact path="/nestedrouter/" component={NestedRouter} />
<Route exact path="/nestedrouter/subroute" component={SubRoute} />
</Switch>
</BrowserRouter>
Simply remove the BrowserRouter from NestedRouter.js file
<Switch>
<Route exact path="/nestedrouter/" component={NestedRouter} />
<Route exact path="/nestedrouter/subroute" component={SubRoute} />
</Switch>
Let's consider this scenario. You have App.jsx as the root file for you ReactJS SPA. In it your render() looks similar to this:
<Switch>
<Route path="/comp" component={MyComponent} />
</Switch>
then, you should be able to use this.props.history inside MyComponent without a problem. Let's say you are rendering MySecondComponent inside MyComponent, in that case you need to call it in such manner:
<MySecondComponent {...props} />
which will pass the props from MyComponent down to MySecondComponent, thus making this.props.history available in MySecondComponent
You need to export the Customers Component not the CustomerList.
CustomersList = withRouter(Customers);
export default CustomersList;
I see that you are using a class component but in case you decide to switch to functional component or encountered the same issue with a functional component in your application, you can fix this issue by using the "useHistory" hook API by react-router-dom.
Example of usage:
import { useHistory } from "react-router-dom";
const Customers = ({customer}) => {
let history = useHistory();
const handleCustomerClick = (customer) => {
history.push(`/customers/${customer.id}`);
}
return (
//some JSX here
);
};
You may find the official documentation here: https://reactrouter.com/web/api/Hooks/usehistory
Beginner's mistake when working with routing is the importance of using withRouter directly with the component and not put any other high order component in between (or at least one that doest not know to push the props.history to its children:
Wrong: export default withRouter(withErrorHandler(Foo));
Correct: export default withErrorHandler(withRouter(Foo));
`const navigate=useNavigate();
navigate(/customers/${customer.id}); `
Don't use with Router.
handleSubmit(e){
e.preventDefault();
this.props.form.validateFieldsAndScroll((err,values)=>{
if(!err){
this.setState({
visible:false
});
this.props.form.resetFields();
console.log(values.username);
const path = '/list/';
this.props.history.push(path);
}
})
}
It works well.
You need to bind handleCustomerClick:
class Customers extends Component {
constructor() {
super();
this.handleCustomerClick = this.handleCustomerClick(this)
}
this.props.history.push(`/customers/${customer.id}`, null);

How to test a React component that uses context like in react-router 2.0 with Jest 0.8.x

I had this problem in the past while using older versions of react-router which I solved using: stubRouterContext + a hacky way to access the component instance (using refs: https://github.com/reactjs/react-router/issues/1140#issuecomment-113174774)
I thought this would improve in the future but I am hitting the same wall with react-router 2.0 (I am not saying this is a problem with react-router but since it uses context, it affects my tests). So, I have a component that uses context to push new state into the url this.context.router.push(...) which is the way to go now
https://github.com/reactjs/react-router/blob/master/upgrade-guides/v2.0.0.md#programmatic-navigation
I am telling jest.dontMock('react-router') but my test will fail with:
TypeError: Cannot read property 'push' of undefined
This happens because the instance returned by TestUtils.renderIntoDocument will have:
context: Object { router: undefined }
Now, what is the real problem here? Is it Jest? I'm pretty sure I am not the only one who encounters this and since stubRouterContext it's not in the official docs of react-router anymore, is there any broad accepted solution for this?
How would I make the test to work? Which is basically having the correct context and being able to access everything from the component instance returned by TestUtils.renderIntoDocument.
I am using react 0.14.7, jest-cli 0.8.2 and react-router 2.0.
Here's the setup that I've ended up with for my context dependent components (stripped down for simplicity, of course):
// dontmock.config.js contains jest.dontMock('components/Breadcrumbs')
// to avoid issue with hoisting of import operators, which causes
// jest.dontMock() to be ignored
import dontmock from 'dontmock.config.js';
import React from "react";
import { Router, createMemoryHistory } from "react-router";
import TestUtils from "react-addons-test-utils";
import Breadcrumbs from "components/Breadcrumbs";
// Create history object to operate with in non-browser environment
const history = createMemoryHistory("/products/product/12");
// Setup routes configuration.
// JSX would also work, but this way it's more convenient to specify custom
// route properties (excludes, localized labels, etc..).
const routes = [{
path: "/",
component: React.createClass({
render() { return <div>{this.props.children}</div>; }
}),
childRoutes: [{
path: "products",
component: React.createClass({
render() { return <div>{this.props.children}</div>; }
}),
childRoutes: [{
path: "product/:id",
component: React.createClass({
// Render your component with contextual route props or anything else you need
// If you need to test different combinations of properties, then setup a separate route configuration.
render() { return <Breadcrumbs routes={this.props.routes} />; }
}),
childRoutes: []
}]
}]
}];
describe("Breadcrumbs component test suite:", () => {
beforeEach(function() {
// Render the entire route configuration with Breadcrumbs available on a specified route
this.component = TestUtils.renderIntoDocument(<Router routes={routes} history={history} />);
this.componentNode = ReactDOM.findDOMNode(this.component);
this.breadcrumbNode = ReactDOM.findDOMNode(this.component).querySelector(".breadcrumbs");
});
it("should be defined", function() {
expect(this.breadcrumbNode).toBeDefined();
});
/**
* Now test whatever you need to
*/
Had same trouble, undefined this.context.history in unit tests.
Stack: React 0.14, react-router 1.0, history 1.14, jasmine 2.4
Was solved next way.
For react-router history I use "history/lib/createBrowserHistory", which allows links without hash. This module is singleton. That means once created it could be used through all your apps.
So I've decided to throw away History mixin and use history directly from separate component next way:
// history.js
import createBrowserHistory from 'history/lib/createBrowserHistory'
export default createBrowserHistory()
// app.js
import React from 'react'
import ReactDOM from 'react-dom'
import { Router, Route, IndexRoute } from 'react-router'
import history from './history'
import Main from './Main'
const Layout = React.createClass({
render() {
return <div>{React.cloneElement(this.props.children)}</div>
}
})
const routes = (
<Route component={Layout} path='/'>
<IndexRoute component={Main} />
</Route>
)
ReactDOM.render(
<Router history={history}>{routes}</Router>,
document.getElementById('my-app')
)
// Main.js
import React from 'react'
import history from './history'
const Main = React.createClass({
next() {
history.push('/next_page')
},
render() {
return <button onClick={this.next}>{'Go to next page'}</button>
}
})
export default Main
Works like a charm and could be tested easily. No more mixins, no more context (at least in this particular case). I believe same approach could be used for standart react-router browser history, but didn't check.

ReactJS - Can't import component

I'm brand new to ReactJS. I'm developing a little single page app and I'm just trying to create my components to import within my main component.
TestComponent.jsx
import React from 'react'
export class TestComponent extends React.Component {
render() {
return (
<div className="content">Test Component</div>
)
}
}
Inside my main.jsx I've imported this component calling
import TestComponent from './components/TestComponent.jsx'
Then I've tried to call my component for a specific route:
render(
(<Router history={history}>
<Route path="/" component={NavMenu}>
<IndexRoute component={Index}/>
<Route path="test" component={TestComponent}></Route>
</Route>
</Router>),
document.getElementById('main')
)
I've not got any errors from the console, but I don't see my component. What am I doing wrong?
The import syntax without curly braces is for importing default exports, not for importing named exports.
Make your component the default export:
TestComponent.jsx
import React from 'react'
export default class TestComponent extends React.Component {
render() {
return (
<div className="content">Test Component</div>
)
}
}
Alternatively you should be able to import it as it is with the following import statement:
import { TestComponent } from './components/TestComponent.jsx'
You might want to read up on ES6 modules (e.g. in Exploring ES6) if you want to use ES6 in your React code.
Make sure to include semicolons after the import statements too. you might get away with a browser (or environment like Node) reading the code in some cases, but the import function runs right into your export in this code.
.js should have the first letter capital. Else import will not take place.

Categories

Resources