I have multiple components which all need to do the same thing. (A simple function which maps over their child components and does something to each one). At the moment I am defining this method in each of the components. But I only want to define it once.
I could define it in the top level component and then pass it down as a prop. But that doesn't feel quite right. It is more a library function than a prop. (It seems to me).
What is the correct way of doing this?
Utils.js with latest Javascript ES6 syntax
Create the Utils.js file like this with multiple functions, etc
const someCommonValues = ['common', 'values'];
export const doSomethingWithInput = (theInput) => {
//Do something with the input
return theInput;
};
export const justAnAlert = () => {
alert('hello');
};
Then in your components that you want to use the util functions, import the specific functions that are needed. You don't have to import everything
import {doSomethingWithInput, justAnAlert} from './path/to/Utils.js'
And then use these functions within the component like this:
justAnAlert();
<p>{doSomethingWithInput('hello')}</p>
If you use something like browserify then you can have an external file i.e util.js that exports some utility functions.
var doSomething = function(num) {
return num + 1;
}
exports.doSomething = doSomething;
Then require it as needed
var doSomething = require('./util.js').doSomething;
If you want to manipulate state in helper functions follow this:
Create a Helpers.js file:
export function myFunc(){ return this.state.name; //define it according to your needs }
Import helper function in your component file:
import {myFunc} from 'path-to/Helpers.js'
In your constructor add that helper function to the class
constructor(){ super() this.myFunc = myFunc.bind(this) }
In your render function use it:
`render(){
{this.myFunc()}
}`
Here are some examples on how you can reuse a function (FetchUtil.handleError) in a React component (App).
Solution 1: Using CommonJS module syntax
module.exports = {
handleError: function(response) {
if (!response.ok) throw new Error(response.statusText);
return response;
},
};
Solution 2: Using "createClass" (React v16)
util/FetchUtil.js
const createReactClass = require('create-react-class');
const FetchUtil = createReactClass({
statics: {
handleError: function(response) {
if (!response.ok) throw new Error(response.statusText);
return response;
},
},
render() {
},
});
export default FetchUtil;
Note: If you are using React v15.4 (or below) you need to import createClass as follows:
import React from 'react';
const FetchUtil = React.createClass({});
Source: https://reactjs.org/blog/2017/04/07/react-v15.5.0.html#migrating-from-reactcreateclass
Component (which reuses FetchUtil)
components/App.jsx
import Categories from './Categories.jsx';
import FetchUtil from '../utils/FetchUtil';
import Grid from 'material-ui/Grid';
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {categories: []};
}
componentWillMount() {
window
.fetch('/rest/service/v1/categories')
.then(FetchUtil.handleError)
.then(response => response.json())
.then(categories => this.setState({...this.state, categories}));
}
render() {
return (
<Grid container={true} spacing={16}>
<Grid item={true} xs={12}>
<Categories categories={this.state.categories} />
</Grid>
</Grid>
);
}
}
export default App;
I'll show two styles below, and you'll want to choose depending on how much the components' logic relate to each other.
Style 1 - Relatively related components can be created with callback references, like this, in ./components/App.js...
<SomeItem
ref={(instance) => {this.childA = instance}}
/>
<SomeOtherItem
ref={(instance) => {this.childB = instance}}
/>
And then you can use shared functions between them like this...
this.childA.investigateComponent(this.childB); // call childA function with childB as arg
this.childB.makeNotesOnComponent(this.childA); // call childB function with childA as arg
Style 2 - Util-type components can be created like this, in ./utils/time.js...
export const getTimeDifference = function (start, end) {
// return difference between start and end
}
And then they can be used like this, in ./components/App.js...
import React from 'react';
import {getTimeDifference} from './utils/time.js';
export default class App extends React.Component {
someFunction() {
console.log(getTimeDifference("19:00:00", "20:00:00"));
}
}
Which to use?
If the logic is relatively-related (they only get used together in the same app), then you should share states between components. But if your logic is distantly-related (i.e., math util, text-formatting util), then you should make and import util class functions.
Another solid option other than creating a util file would be to use a higher order component to create a withComponentMapper() wrapper. This component would take in a component as a parameter and return it back with the componentMapper() function passed down as a prop.
This is considered a good practice in React. You can find out how to do so in detail here.
Sounds like a utility function, in that case why not put it in a separate static utility module?
Otherwise if using a transpiler like Babel you can make use of es7's static methods:
class MyComponent extends React.Component {
static someMethod() { ...
Or else if you are using React.createClass you can use the statics object:
var MyComponent = React.createClass({
statics: {
customMethod: function(foo) {
return foo === 'bar';
}
}
However I don't advise those options, it doesn't make sense to include a component for a utility method.
Also you shouldn't be passing a method down through all your components as a prop it will tightly couple them and make refactoring more painful. I advise a plain old utility module.
The other option is to use a mixin to extend the class, but I don't recommend that as you can't do it in es6+ (and I don't see the benefit in this case).
Shouldn't you use a Mixin for this ? See https://facebook.github.io/react/docs/reusable-components.html
Although they are falling out of favour see https://medium.com/#dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750
Might be useful
Using react-alert module for alerts
My code looks like this -
index.js:
const options = {
// you can also just use 'bottom center'
position: positions.TOP_CENTER,
timeout: 5000,
offset: '30px',
type: types.ERROR,
// you can also just use 'scale'
transition: transitions.FADE
}
ReactDOM.render(<AlertProvider template={AlertTemplate} {...options}>
<App /></AlertProvider>, document.getElementById('root'));
App.js
class App extends React.Component { //then my state, functions, constructors,
//here is the problem
nextClicked = (e) => {
if (//something) {
if (//something) {
const alert = useAlert();
alert.show("ERROR MESSAGE!!!");
}
} // etc
export default withAlert()(App)
Basically, I am getting the error
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/warnings/invalid-hook-call-warning.html for tips about how to debug and fix this problem.
From their docs, it says you can use it with a higher order-component. So, if you
import the withAlert module from react-alert, you can wrap your component when you export it. Again, check the docs on github, this is covered.
Converting the example from the docs to a class component, you get:
import React from 'react'
import { withAlert } from 'react-alert'
class App extends React.component {
constructor(props) {
super(props);
}
render {
return (
<button
onClick={() => {
this.props.alert.show('Oh look, an alert!')
}}
>
Show Alert
</button>
)
}
}
export default withAlert()(App)
Because you wrap the component in the HOC, you get access to the alert prop.
I've taken next code from here: https://www.meteor.com/tutorials/react/adding-user-accounts.
Can I replace in this particular case
ReactDOM.findDOMNode(this.refs.container)) with
this.refs.container
without any hidden bugs in future?
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Template } from 'meteor/templating';
import { Blaze } from 'meteor/blaze';
export default class AccountsUIWrapper extends Component {
componentDidMount() {
// Use Meteor Blaze to render login buttons
this.view = Blaze.render(Template.loginButtons,
ReactDOM.findDOMNode(this.refs.container));
}
componentWillUnmount() {
// Clean up Blaze view
Blaze.remove(this.view);
}
render() {
// Just render a placeholder container that will be filled in
return <span ref="container" />;
}
}
Or maybe even change using callback function:
....
export default class AccountsUIWrapper extends Component {
componentDidMount() {
// Use Meteor Blaze to render login buttons
this.view = Blaze.render(Template.loginButtons,
this.container);
}
....
render() {
// Just render a placeholder container that will be filled in
return <span ref={(node) => (this.container = node) />;
}
}
As suggested in the react refs documentation
If you worked with React before, you might be familiar with an older
API where the ref attribute is a string, like "textInput", and the DOM
node is accessed as this.refs.textInput. We advise against it because
string refs have some issues, are considered legacy, and are likely to
be removed in one of the future releases. If you're currently using
this.refs.textInput to access refs, we recommend the callback pattern
instead.
Hence ref callback is the right way to go if you want to have future support
export default class AccountsUIWrapper extends Component {
componentDidMount() {
this.view = Blaze.render(Template.loginButtons,
this.container);
}
....
render() {
return <span ref={(node) => (this.container = node) />;
}
}
I'm trying to add a React map component to my project but run into an error. I'm using Fullstack React's blog post as a reference. I tracked down where the error gets thrown in google_map.js line 83:
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
Here is my map component so far. The page loads just fine (without a map) when I comment out lines 58-60, the last three lines. edit: I made the changes that #Dmitriy Nevzorov suggested and it still gives me the same error.
import React from 'react'
import GoogleApiComponent from 'google-map-react'
export class LocationsContainer extends React.Component {
constructor() {
super()
}
render() {
const style = {
width: '100vw',
height: '100vh'
}
return (
<div style={style}>
<Map google={this.props.google} />
</div>
)
}
}
export class Map extends React.Component {
componentDidUpdate(prevProps, prevState){
if (prevProps.google !== this.props.google){
this.loadMap();
}
}
componentDidMount(){
this.loadMap();
}
loadMap(){
if (this.props && this.props.google){
const {google} = this.props;
const maps = google.maps;
const mapRef = this.refs.map;
const node = ReactDOM.findDOMNode(mapRef);
let zoom = 14;
let lat = 37.774929
let lng = 122.419416
const center = new maps.LatLng(lat, lng);
const mapConfig = Object.assign({}, {
center: center,
zoom: zoom
})
this.map = new maps.Map(node, mapConfig)
}
}
render() {
return (
<div ref='map'>
Loading map...
</div>
)
}
}
export default GoogleApiComponent({
apiKey: MY_API_KEY
})(LocationsContainer)
And here is where this map component gets routed in main.js:
import {render} from 'react-dom';
import React from 'react';
import Artists from './components/Artists'
import { Router, Route, Link, browserHistory } from 'react-router'
import Home from './components/HomePage'
import Gallery from './components/ArtGallery'
import ArtistPage from './components/ArtistPage'
import FavsPage from './components/FavsPage'
import LocationsContainer from './components/Locations'
//Create the route configuration
render((
<Router history={browserHistory}>
<Route path="/" component={Home} />
<Route path="locations" component={LocationsContainer} />
<Route path="artists" component={Artists} />
<Route path="gallery" component={Gallery} />
<Route path="favorites" component={FavsPage} />
<Route path=":artistName" component={ArtistPage} />
</Router>
), document.getElementById('app'))
For me it happened when I forgot to write extends React.Component at the end.
I know it's not exactly what YOU had, but others reading this answer can benefit from this, hopefully.
For me it was because I forgot to use the new keyword when setting up Animated state.
eg:
fadeAnim: Animated.Value(0),
to
fadeAnim: new Animated.Value(0),
would fix it.
Edit from 5 years on with more explanation:
The most likely issue is that you're missing the new keyword somewhere, just like I did above. What this means is you don't even need to be using React to come across this error.
The issue is that in JS you can create classes like so (Example from MDN):
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
calcArea() {
return this.height * this.width;
}
}
If you wanted to use this class, you need to create a new instance of it like so:
const rect = new Rect(height, width);
The reason for this problem is often you're trying to do a function call to the definition of the class (or something inside the definition), rather than an instance of the class.
Essentially, in code, you're doing this:
Rectangle.calcArea() // incorrect!
when you should be doing
rect.calcArea() // correct!
tl;dr
If you use React Router v4 check your <Route/> component if you indeed use the component prop to pass your class based React component!
More generally: If your class seems ok, check if the code that calls it doesn't try to use it as a function.
Explanation
I got this error because I was using React Router v4 and I accidentally used the render prop instead of the component one in the <Route/> component to pass my component that was a class. This was a problem, because render expects (calls) a function, while component is the one that will work on React components.
So in this code:
<HashRouter>
<Switch>
<Route path="/" render={MyComponent} />
</Switch>
</HashRouter>
The line containing the <Route/> component, should have been written like this:
<Route path="/" component={MyComponent} />
It is a shame, that they don't check it and give a usable error for such and easy to catch mistake.
Happened to me because I used
PropTypes.arrayOf(SomeClass)
instead of
PropTypes.arrayOf(PropTypes.instanceOf(SomeClass))
For me, it was ComponentName.prototype instead of ComponentName.propTypes.
auto suggested by Phpstorm IDE. Hope it will help someone.
You have duplicated export default declaration. The first one get overridden by second one which is actually a function.
I experienced the same issue, it occurred because my ES6 component class was not extending React.Component.
Mostly these issues occur when you miss extending Component from react:
import React, {Component} from 'react'
export default class TimePicker extends Component {
render() {
return();
}
}
For me it was because i used prototype instead of propTypes
class MyComponent extends Component {
render() {
return <div>Test</div>;
}
}
MyComponent.prototype = {
};
it ought to be
MyComponent.propTypes = {
};
Post.proptypes = {
}
to
Post.propTypes = {
}
someone should comment on how to monitor such error in a very precise way.
Two things you can check is,
class Slider extends React.Component {
// Your React Code
}
Slider.propTypes = {
// accessibility: PropTypes.bool,
}
Make sure that you extends React.Component
Use propTypes instead of prototype (as per IDE intellisense)
Looks like there're no single case when this error appears.
Happened to me when I didn't declare constructor in statefull component.
class MyComponent extends Component {
render() {
return <div>Test</div>;
}
}
instead of
class MyComponent extends Component {
constructor(props) {
super(props);
}
render() {
return <div>Test</div>;
}
}
This is a general issue, and doesn't appear in a single case. But, the common problem in all the cases is that you forget to import a specific component (doesn't matter if it's either from a library that you installed or a custom made component that you created):
import {SomeClass} from 'some-library'
When you use it later, without importing it, the compiler thinks it's a function. Therefore, it breaks. This is a common example:
imports
...code...
and then somewhere inside your code
<Image {..some props} />
If you forgot to import the component <Image /> then the compiler will not complain like it does for other imports, but will break when it reaches your code.
In file MyComponent.js
export default class MyComponent extends React.Component {
...
}
I put some function related to that component:
export default class MyComponent extends React.Component {
...
}
export myFunction() {
...
}
and then in another file imported that function:
import myFunction from './MyComponent'
...
myFunction() // => bang! "Cannot call a class as a function"
...
Can you spot the problem?
I forgot the curly braces, and imported MyComponent under name myFunction!
So, the fix was:
import {myFunction} from './MyComponent'
I received this error by making small mistake. My error was exporting the class as a function instead of as a class. At the bottom of my class file I had:
export default InputField();
when it should have been:
export default InputField;
For me, it was because I'd accidentally deleted my render method !
I had a class with a componentWillReceiveProps method I didn't need anymore, immediately preceding a short render method. In my haste removing it, I accidentally removed the entire render method as well.
This was a PAIN to track down, as I was getting console errors pointing at comments in completely irrelevant files as being the "source" of the problem.
I had a similar problem I was calling the render method incorrectly
Gave an error:
render = () => {
...
}
instead of
correct:
render(){
...
}
I had it when I did so :
function foo() (...) export default foo
correctly:
export default () =>(...);
or
const foo = ...
export default foo
For me it happened because I didn't wrap my connect function properly, and tried to export default two components
I faced this error when I imported the wrong class and referred to wrong store while using mobx in react-native.
I faced error in this snippet :
import { inject, Observer } from "mobx-react";
#inject ("counter")
#Observer
After few corrections like as below snippet. I resolved my issue like this way.
import { inject, observer } from "mobx-react";
#inject("counterStore")
#observer
What was actually wrong,I was using the wrong class instead of observer I used Observer and instead of counterStore I used counter. I solved my issue like this way.
I experienced this when writing an import statement wrong while importing a function, rather than a class. If removeMaterial is a function in another module:
Right:
import { removeMaterial } from './ClaimForm';
Wrong:
import removeMaterial from './ClaimForm';
I have also run into this, it is possible you have a javascript error inside of your react component. Make sure if you are using a dependency you are using the new operator on the class to instantiate the new instance. Error will throw if
this.classInstance = Class({})
instead use
this.classInstance = new Class({})
you will see in the error chain in the browser
at ReactCompositeComponentWrapper._constructComponentWithoutOwner
that is the giveaway I believe.
In my case i wrote comment in place of Component by mistake
I just wrote this.
import React, { Component } from 'react';
class Something extends Component{
render() {
return();
}
}
Instead of this.
import React, { Component } from 'react';
class Something extends comment{
render() {
return();
}
}
it's not a big deal but for a beginner like me it's really confusing.
I hope this will be helpfull.
In my case, using JSX a parent component was calling other components without the "<>"
<ComponentA someProp={someCheck ? ComponentX : ComponentY} />
fix
<ComponentA someProp={someCheck ? <ComponentX /> : <ComponentY />} />
Another report here: It didn't work as I exported:
export default compose(
injectIntl,
connect(mapStateToProps)(Onboarding)
);
instead of
export default compose(
injectIntl,
connect(mapStateToProps)
)(Onboarding);
Note the position of the brackets. Both are correct and won't get caught by either a linter or prettier or something similar. Took me a while to track it down.
In my case, I accidentally put component name (Home) as the first argument to connect function while it was supposed to be at the end. duh.
This one -surely- gave me the error:
export default connect(Home)(mapStateToProps, mapDispatchToProps)
But this one worked -surely- fine:
export default connect(mapStateToProps, mapDispatchToProps)(Home)
This occured when I accidentally named my render function incorrectly:
import React from 'react';
export class MyComponent extends React.Component {
noCalledRender() {
return (
<div>
Hello, world!
</div>
);
}
}
My instance of this error was simply caused because my class did not have a proper render method.
Actually all the problem redux connect. solutions:
Correct:
export default connect(mapStateToProps, mapDispatchToProps)(PageName)
Wrong & Bug:
export default connect(PageName)(mapStateToProps, mapDispatchToProps)
In my scenario I was attempting to use hot reloading on a custom hook (not sure why, probably just muscle memory when creating components).
const useCustomHook = () => {
const params = useParams();
return useSelector(
// Do things
);
};
// The next line is what breaks it
export default hot(module)(useCustomHook);
The correct way
const useCustomHook = () => {
const params = useParams();
return useSelector(
// Do things
);
};
export default useCustomHook;
Apparently you can't hot reload hook 😅 😂
In my case I accidentally called objectOf
static propTypes = {
appStore: PropTypes.objectOf(AppStore).isRequired
}
Instead of instanceOf:
static propTypes = {
appStore: PropTypes.instanceOf(AppStore).isRequired
}