Recently i started new project and upgraded for it libraries to newest including react. I encountered first problem when accessing passed params from dynamic route inside class component. In the past in order to do it, one would need to wrap exported class component in withRouter function returned from router. In the documentation they say that this functionality has been removed in v6 and if you need it, it can be recreated manually docs link.
I created with-router.jsx file and pasted their code:
import {
useLocation,
useNavigate,
useParams,
} from "react-router-dom";
function withRouter(Component) {
function ComponentWithRouterProp(props) {
let location = useLocation();
let navigate = useNavigate();
let params = useParams();
return (
<Component
{...props}
router={{ location, navigate, params }}
/>
);
}
return ComponentWithRouterProp;
}
next i added it to my class component:
import React, { Component } from 'react';
import withRouter from './with-router';
class Product extends Component {
render() {
return (
<div className="product">...</div>
);
}
}
export default withRouter(Product);
and it does not work, there is following error:
Compiled with problems:
WARNING in ./src/components/product.jsx 67:15-25
export 'default' (imported as 'withRouter') was not found in './with-router' (module has no exports)
so it does not seem like their own code is working, maybe someone has an idea how to make it work? another thing is to consider future implications, functionality deleted without replacement and if you need it - recreate it? why remove if you have to manually add it anyway, does not make sense with react recently.
"react": "^18.2.0"
"react-dom": "^18.2.0"
"react-router-dom": "^6.4.4"
"webpack": "^5.74.0"
"webpack-cli": "^4.10.0"
"webpack-dev-server": "^4.11.1"
As the error points out, it seems you neglected to export your custom withRouter HOC.
Compiled with problems: WARNING in ./src/components/product.jsx
67:15-25 export 'default'* (imported as 'withRouter') was not found in
'./with-router' (module has no exports*)
* Emphasis is mine
Assuming you've shared the complete with-router.jsx file contents, it's missing a default export.
import {
useLocation,
useNavigate,
useParams,
} from "react-router-dom";
function withRouter(Component) {
function ComponentWithRouterProp(props) {
const location = useLocation();
const navigate = useNavigate();
const params = useParams();
return (
<Component
{...props}
router={{ location, navigate, params }}
/>
);
}
return ComponentWithRouterProp;
}
export default withRouter; // <-- add default export!
so it does not seem like their own code is working, maybe someone has
an idea how to make it work?
The RRD code is well maintained and tested, I've not run across many blatant issues/bugs with their React hooks.
another thing is to consider future implications, functionality
deleted without replacement and if you need it - recreate it? why
remove if you have to manually add it anyway, does not make sense with
react recently.
I think it does make sense with the direction React is going.
React has made it clear that Function components and React hooks are the future of React (for now) and that Class components are, for all intents and purposes, deprecated, though they are kept around for compatibility reasons. The functionality you describe as being "deleted", i.e. removed, was replaced... by the new React hooks, and the FAQ doc you referenced is made available as a compatibility bridge if you are using current RRDv6 components with older React code. Creating this HOC is trivial, if you need it, but if the main objective is to create React function components that use React hooks then there's no need or desire for RRD to export a withRouter HOC of their own that encourages "deprecated" React component coding patterns.
A good general rule here would be to use your new withRouter component on your older class components, and for any new components you create implement them as React Function components and use the React hooks. If you want you can rewrite/convert older class components to function components, but this is basically a "only if you really need/want to" and not a "don't just convert for the sake of converting" type of thing.
Related
I am fairly new to React and still wrapping my head around custom-hooks. I cam across a code where a custom hook was created to handle the component imports.
useComponentPalette.js
import {TodoEditor} from './components/TodoEditor'
import {TodoItem} from './components/TodoItem'
import {TodoList} from './components/TodoList'
import {CheckBox} from './components/CheckBox'
const defaultComponents = {
TodoEditor,
TodoItem,
TodoList,
CheckBox
}
export function useComponentPalette(){
return defaultComponents
}
And then in order to use the hook,
const {TodoItem, TodoList, Checkbox } = useComponentPalette()
My Question :- Does this approach provides any advantage over the regular imports in the component ? or this is an anti-pattern ?
How I usually import the components is as follows
import {TodoEditor} from './components/TodoEditor'
import {TodoItem} from './components/TodoItem'
import {TodoList} from './components/TodoList'
import {CheckBox} from './components/CheckBox'
function App(){
return(
<>
<TodoList/>
</>
)
}
It's not a good idea to use react hooks like this you can get the same result without react hook
// first file name.js
import {TodoEditor} from './components/TodoEditor'
import {TodoItem} from './components/TodoItem'
import {TodoList} from './components/TodoList'
import {CheckBox} from './components/CheckBox'
export default {
TodoEditor,
TodoItem,
TodoList,
CheckBox
}
//component file
import * as Component form 'first file name';
//<Component.TodoEditor/>
//or
import {TodoEditor} form 'first file name';
The way that I use react-hooks is for making my code more dry and increase it's readability, so react-hooks is not good fit for this kind of usage.
Hi #Sachin,
In my option, React JS use hook to manage reuse stateful logic between components. In other word, Hooks do well to encapsulating state and share logic. If you want to do some stateful logic or condition base logic with these components, then it's fine with that. But if you are using just without condition in the given components. Then, This Is useless for making the custom hook. You can do that without a custom hook in a simpler way.
Here is a simple way to do that:-
In components folder. I create index file, this is the entry point of all my exporting components
In that file. I export all my components, as you can see.
I use that components like this. It much better way. In my option.
import { Header, Footer, Sider } from "./components"
before using react custom hooks, we should be aware of the rationale behind it.
Customs hooks functionality was provided to reuse stateful logic. If logic doesn't require any state, we will use simple functions and if it is about components only there there are different patterns for making code general and scaleable.
So, there is no usage of custom hook in above case at all. For me, I would go with the following code for above scenario:
// components/index.tsx
import {Todo} from './todo'
import {CheckBox} from './components/CheckBox'
export {
Todo,
CheckBox
}
// componentns/todo/index.tsx
import {Editor} from './Editor'
import {Item} from './Item'
import {List} from './List'
const Todo = {
Editor,
Item,
List
}
export default Todo;
and usage will be like
import { Checkbox, Todo } from "components"
...
<Checkbox ... />
<Todo.List ...>
<Todo.Item ... >
</Todo.Editor ... />
</Todo.Item ... >
</Todo.List>
...
P.S Usage can be different based upon the logic of components, just giving an hint how we can patterns to serve our purpose.
Hope it helps.
I used npm init react-app appname which creates, among other files, App.js. In that file is a function component:
function App() {
return (
<SomeJSX />
);
}
I edited the function component into a class component, like so:
class App extends React.Component{
render() {
return (
<TheSameJSX />
);
}
}
Now, when I run npm start, I get an error:
Failed to compile
src/App.js
Line 4:19: 'React' is not defined no-undef
Search for the keywords to learn more about each error.
I imagine I need to add some setting somewhere that will automatically include React without me needing to explicitly import it at the top of every file. How do I do this? And why does this npm package not do that by default? I know a bit about javascript (and html and css), and have read a bit about React, but I am completely unaware of how npm or webpack works.
Thanks in advance!
EDIT: To clarify, I know how to import stuff with javascript. I can easily add import React from 'react'; to the file and make it work. However, I find it difficult to believe that adding an import statement to every single javascript file is the recommended method, and I don't understand why this example app wouldn't be set up so as to avoid having to do that. Am I mistaken? Do I really need to manually import the same thing over and over again within the same project? Could I set a global variable to React so that I can use it from wherever?
In your default function component you're not extending any classes and just writing a simple function
function App() {
return (
<SomeJSX />
);
}
In class component, you're in fact extending the Class Component by React.Component provided by React default export object and hence you must import it from the package
//only use one of these
import * as React from "react";
import {Component} from "react"; // you can directly extend without writing `React.` with this import
import React from "react"
So your code would be
import React from "react";
class App extends React.Component{
render() {
return (
<TheSameJSX />
);
}
}
Any of the above imports should be fine with a preference to the first and second one.
I am very new to Vue and I have read an article or two about it (probably vaguely).
Also, Since I have some understanding of react, I tend to assume certain things to work the same way (but probably they do not)
Anyway, I just started with Quasar and was going through the Quasar boilerplate code
In the myLayout.vue file, I see being used inside my template
<template>
<q-layout view="lHh Lpr lFf">
<q-layout-header>
<q-toolbar
color="negative"
>
<q-btn
flat
dense
round
#click="leftDrawerOpen = !leftDrawerOpen"
aria-label="Menu"
>
<q-icon name="menu" />
</q-btn>
based on my vaguely understanding, I thought for every component we are using to whom we need to pass props we need to import it as well but unfortunately I can't see it in my import-script area
<script>
import { openURL } from 'quasar'
export default {
name: 'MyLayout',
data () {
return {
leftDrawerOpen: this.$q.platform.is.desktop
}
},
methods: {
openURL
}
}
</script>
I would've thought the script to be something like
<script>
import { openURL } from 'quasar'
import {q-icon} from "quasar"
or at least something like that but here we only have
import { openURL } from 'quasar'
Also, Even if we remove the above snippet, our boilerplate app looks to be working fine so here are my two questions
Question 1: What is the use of import { openURL } from 'quasar' (like what it does)
Question 2: How can template contain <quasar-icon> or <quasar-whatever> without even importing it in script tag?
How can template contain <quasar-icon> or <quasar-whatever> without even importing it in script tag?
There are two ways to import components. The first way (which I recommend, and being most similar to React) is to import the component and add it to the components option inside the component that you want to use it within.
App.vue
<div>
<my-component/>
</div>
import MyComponent from 'my-component'
export default {
components: {
MyComponent
}
}
The second way is to import it globally for use within any Vue component in your app. You need only do this once in the entry script of your app. This is what Quasar is doing.
main.js
import Vue from 'vue'
import MyComponent from 'my-component'
Vue.component('my-component', MyComponent)
What is the use of import { openURL } from 'quasar' (like what it does)
I'm not familiar with Quasar, so I can't give you a specific answer here (I don't know what openURL does). You should check the Quasar docs.
openURL is being used as a method here. Perhaps it is being called from somewhere in the template (which you have excluded from the question).
A1) Import statement is 1 way (es6) way to split your code into different files and then import functions/objects/vars from other files or npm modules see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
A2) Vue allows 2 mechanisms to register components. Global and local. Globally registered components does not have to be imported and registered in every component before use (in template or render fn). See URL from comment above https://v2.vuejs.org/v2/guide/components-registration.html#Global-Registration
Following up the issue on Github, I have a component Comp that when exported, is wrapped with injectSheet from reactjss. Please see the setup on codesandbox.
In a unit test, I'd like to assert that that component contains <a>, which it does (see codesandbox), but the test fails regardless:
describe("<Comp /> component", () => {
const wrapper = shallow(<Comp />);
it("should render a <a>", () => {
expect(wrapper.find('a')).to.have.length(1);
});
});
I get Error: [undefined] Please use ThemeProvider to be able to use WithTheme. So my natural (perhaps not the correct?) reaction was to wrap the component with ThemeProvider:
const wrapper = shallow(
<ThemeProvider theme={{}}>
<Comp />
</ThemeProvider>
)
Then I get AssertionError: expected { length: 0 } to have a length of 1 but got 0.
I tried a whole slew of approaches, including calling dive, find or first with an extra shallow call, but I would always end up with Please use ThemeProvider to be able to use WithTheme:
// 1. dive(), as suggested in
// https://github.com/cssinjs/react-jss/issues/30#issuecomment-268373765
expect(wrapper.dive('Comp')).to.have.length(1);
expect(wrapper.dive('Comp').find('a')).to.have.length(1);
expect(wrapper.dive().find('a')).to.have.length(1);
// 2. find() / first(), as suggested in https://github.com/airbnb/enzyme/issues/539
expect(wrapper.find(Comp).shallow().find('a')).to.have.length(1);
expect(wrapper.first().shallow().find('a')).to.have.length(1);
Any ideas here? I am a bit new to unit testing with React, so I would appreciate if someone could enlighten me on this ;)
For anyone still struggling with this, one viable approach was suggested on GitHub. Instead of testing the styled component wrapped with injectSheet HOC, you export your stand-alone component and test it in isolation
// Component.js
import React from 'react'
import injectSheet from 'react-jss'
const styles = {
color: 'burlywood'
}
// named export for unit tests
export const Component = props => <h1>Component</h1>
// default export to be used in other components
export default injectSheet(styles)(Component)
which would work for most use cases, since more often than not, you need to unit test the plain component and its logic, and not any of its associated styling. So in your unit test just do
import { Component } from './Component'
instead of (which you would do in the rest of your codebase)
import Component from './Component'
I am using react-router 2.4.0 and want to link to another route programmatically (what I did before using <Link>).
It's explained nicely in this SO post where they say in 2.4.x you should use the decorator pattern with withRouter, so I am using the following code:
import {withRouter} from 'react-router' // further imports omitted
class CreateJobItemFormRaw extends React.Component {
...
}
const CreateJobItemForm = withRouter(CreateJobItemFormRaw)
export default CreateJobItemForm
Then in other files, I use
import CreateJobItemForm from './CreateJobItemForm'
However, with this approach my app doesn't render at all any more and the console outputs:
CreateJobItemForm.js:76 Uncaught TypeError: (0 , _reactRouter.withRouter) is not a function
Can anyone help me solve this?
I trust that you are in fact using react-router 2.4.0, but in my case it was worth double-checking that my package.json did in fact enforce that version. I modified my package.json as such:
"dependencies": {
"react-router": "^2.4.0",
...
}
Hope this helps.
In my case, I upgraded to react-router v6 and discovered that withRouter was removed and hooks should be used instead.
Upgrade documentation here:
https://reactrouter.com/en/main/upgrading/v5
Along with the upgrade to v5.1, you should replace any usage of withRouter with hooks.
Here is a detailed guide to hooks:
https://reacttraining.com/blog/react-router-v5-1/
In a comment to another answer you linked to this question and said that you're trying to navigate using react-router 2.4+. Try and put the PropType specifications in the file and see if that gives you any warnings. For Example:
// PropTypes
Example.propTypes = {
router: React.PropTypes.shape({
push: React.PropTypes.func.isRequired
}).isRequired
};
import { withRouter } from 'react-router-dom'
react-router v4.x