I know the title is maybe a bit confusing. Here's a code sample:
//First.js
export default class First extends React.Component {
constructor(props) {
super(props);
module.exports.push = route => {
this.refs.router.push(route)
}
module.exports.pop = () => {
this.refs.router.pop()
}
}
render() {
return <Router ref="router"/>
}
}
and then
//second.js
import { push, pop } from "first.js"
//class instantiation and other code
push("myRoute")
Code pen: https://codepen.io/Stefvw93/pen/bLyyNG?editors=0010
The intent is to avoid using the withRouter function from react-router. So instead, expose push/pop history functions from a single instance of react-router's browserRouter component. It works by creating a reference to the router instance (ref="router") and then exporting this instance by doing something like module.exports.push=this.refs.router.push
Since you can not declare any dynamic exports, the only way would to export push and pop would be to export some sort of mutable container object first, and then modify it afterwards. e.g Export immediately an empty object, and set its push and pop properties in the constructor.
//First.js
export const router = {};
export default class First extends React.Component {
constructor(props) {
super(props);
router.push = route => {
this.refs.router.push(route)
};
router.pop = () => {
this.refs.router.pop()
};
}
render() {
return <Router ref="router"/>
}
}
and then
//second.js
import { router } from "first.js"
//class instantiation and other code
router.push("myRoute")
But there are big downsides of doing it this way:
you’ll end up using last rendered router instance
there’s no checking if such instance already exists
you can not have multiple routers using the same pattern
I’d prefer being explicit and writing withRouter wherever I need router, because:
it is not subject to race conditions — you either have a router or you don’t, in which case you get a nice error log
it communicates your intention clearly, and documentation about withRouter exists
uses well—known and elegant HoC pattern
allows you to have multiple routers
Long story short, mutable global state is bad, just don’t do it.
Related
I have a class in my react app which uses 'Builder Pattern' and I've imported it in some nested components in my page. for example in parent component and child component, too.
But it seems it calls the class's constructor once! and in second instantiate it has existing data which I've added in previews in previews instantiate. (Not a new and clean instantiate!)
// [Builder pattern]
class Requester {
constructor() {
this.definedHeaders = {}
console.log('construct', this)
}
contentType(contenttype) {
this.definedHeaders['Content-Type'] = contenttype
return this;
}
async get(url) {
// ...
}
async post(url, data = {}, isFormData = false) {
// ...
}
}
export default new Requester();
ES modules are evaluated only once, when they are imported the first time. This trait is common to all JavaScript module implementations.
Because of that, exporting class instance is a common way to make class a singleton. That a class itself isn't exported prevents from creating additional instances.
If the intention is to not have a single instance, a class itself should be exported:
export default class Requester {...}
And instantiated in places where it's used:
import Requester from '...';
const requester = new Requester;
ESLint is giving me this error on a react project.
Component should be written as a pure function (react prefer/stateless
function)
It points to the first line of the component.
export class myComponent extends React.Component {
render() {
return (
//stuff here
);
}
}
How do I get rid of this error?
Two choices.
Temporarily disable warning
(Untested; and there are multiple ways to do this.)
// eslint-disable-next-line react/prefer-stateless-function
export class myComponent extends React.Component {
...
}
Use a pure stateless component
The return value is what will be rendered (e.g., you're basically writing class-based component's render method:
export const myComponent = () => {
return (
// JSX here
)
}
(Or use non-ES6 notation if that's your thing.)
For components like this with no other supporting logic I prefer the implicit return, e.g.,
export MyComponent = () =>
<div>
// Stuff here
</div>
This is a matter of preference. I would say that you should follow React naming conventions, though, and keep all components starting with an upper-case letter.
ESLint may complain about missing parens around a multi-line JSX expressions, so disable that rule or use parens.
If you need props, they're passed in as the argument to the function:
const MyComponent = (props) =>
<div>
<Something someProp={props.foo} />
</div>
export MyComponent
And you can destructure in the parameter as usual for convenience:
const MyComponent = ({ foo }) =>
<div>
<Something someProp={foo} />
</div>
This can make the implicit return a little easier if you were using local vars. You'll get an ESLint warning about missing PropTypes unless you declare them; since it's not a class you cannot simply use static propTypes in the class, they must be attached to the function (which many people prefer anyway).
Add constructor() like:
exports class myComponent extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<div>Hello</div>
);
}
}
Write your component as a stateless function:
export myComponent = () => { //stuff here };
There are actually two styles of defining components in React: Functional components (which are just functions from props to a React component) and class components.
The main difference between them is that class components can have state and lifecycle methods such as componentDidMount, componentDidUpdate, etc.
Whenever you don't need state of lifecycle methods, you should write your component as a stateless function, as stateless components are in general easier to reason about.
To write a functional component, you write a function that takes a single argument. This argument will receive the component's props. Consequently, you don't use this.props to access the component's props - you just use the function's argument.
If you rely on props, then there is a better (somewhat arguable, as of this writing) way to fix this error without writing out Stateless functions - by writing a PureComponent and using this eslint rule [source]:
"react/prefer-stateless-function": [2, { "ignorePureComponents": true }],
With above rule, the following snippet is valid (since it depends on props)
class Foo extends React.PureComponent {
render() {
return <div>{this.props.foo}</div>;
}
}
React team plans to build optimizations around SFC but they are not there yet. So until that happens, SFCs will not offer any benefits over PureComponents. In fact, they will be slightly worse as they will not prevent wasteful renders.
You will get this error only when your class does not have any life cycle method or constructor.
To solve this either you have to disable the lint property or make it as a pure function or create constructor for the class.
const myComponent = () => {
return (
//stuff here
);
};
export default myComponent;
And in app.js file just import this component as we do for class like
import myComponent from './myComponent.js'
and call as
<myComponent />
It will work for sure.
export class myComponent extends PureComponent {
...
}
If all you're doing is rendering a jsx template, and not declaring state with constructor(props), then you should write your component as a pure function of props, and not use the class keyword to define it.
ex.
export const myComponent = () => (
// jsx goes here
);
you need to add constructor(props)
export class myComponent extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
//stuff here
);
}
}
I have an isomorphic React application, with components being rendered on the server-side. I wish to use a 3rd party React component: (GraphiQL), and am rendering as such:
var GraphiQLComponent = React.createElement(GraphiQL, { fetcher: graphQLFetcher}, "");
router.get('/graphiql', function (req, res) {
res.send(ReactDOMServer.renderToString(GraphiQLComponent));
});
However, this component uses the window object: window.localStorage and window.addEventListener, and when I try to load the page in the browser, I get the error:
ReferenceError: window is not defined
Can I render React components which use the window object, on the server? If so, what do I need to do to resolve this error?
Don't use libraries that depend on window in your components.
or
Only use these libraries in componentDidMount and exclude them for prerendering (make sure your component don't render different in prerendering or it doesn't work).
I figured I shouldn’t import the library outside the class definition, but instead require it in the componentDidMount method or a called method thereof.
So, instead of:
...
import MyWindowDependentLibrary from 'path/to/library';
...
export default class MyClass extends React.Component {
...
componentDidMount() { MyWindowDependentLibrary.doWork(); }
...
}
I did:
// removed import
...
export default class MyClass extends React.Component {
...
componentDidMount() {
const MyWindowDependentLibrary = require( 'path/to/library' );
MyWindowDependentLibrary.doWork();
}
...
}
Yes you can!
Just do not run that piece of code until window is defined!
I also imagine you could use those functions in a componentDidMount() lifecycle function.
if (window !== 'undefined') {
// do your window required stuff
}
I've used this multiple times in components being rendered server-side, and it works like a charm.
I am learning to test React stateless components using the ReactTestUtils library. This is my simple component:
import React from 'react';
const Greeter = ({name,place}) => (
<h1>Hello,{name}. Welcome to the {place}.</h1>
);
export default Greeter;
This is my test spec, to get the renderIntoDocument working, I wrapped my Greeter component in a div as suggested here:
import {expect} from 'chai';
import React from 'react';
import ReactTestUtils from 'react-addons-test-utils';
import Greeter from '../Greeter';
describe('Greeter Components',() => {
it('renders correctly',() => {
var component = ReactTestUtils.renderIntoDocument(<div>
<Greeter name="Vamsi" place="Hotel California"/>
</div>);
var hasH1 = ReactTestUtils.findRenderedDOMComponentWithTag(component,'h1');
expect(hasH1).to.be.ok;
});
});
I get the error
findAllInRenderedTree(...): instance must be a composite component.
I am providing my code as jsbin here.
Since function components don't have an instance associated with them, you can't use them directly with render or renderIntoDocument. Attempting to wrap the function component is a good idea, unfortunately using a div doesn't work for a similar reason. DOM components also don't return a component instance, instead they return the underlying DOM node.
Which is all to say that you can't use the test utils function or native components as the "root" component you are rendering. Instead you will want to wrap your function components in a wrapper component that uses createClass or extends React.Component.
class Wrapper extends React.Component {
render() {
return this.props.children
}
}
let component = renderIntoDocument(<Wrapper><Greeter /></wrapper>
Gotcha's like this may be reason enough to make use of a third-party testing library like the popular enzyme, or my own take: teaspoon. Both abstract over issues like this by seamlessly wrapping and unwrapping function components for you, so you don't need to worry about what type of component you are trying to render.
Wrapping functional components in a <div> works for me. You just have to search for the component you want to test a little differently, i.e.
const props = { p1: "1" }
test('Foo renders correctly classed div', () => {
const cpt = TestUtils.renderIntoDocument(<div><Foo {...props} /></div>);
const myNode = ReactDOM.findDOMNode(cpt.childNodes[0]);
expect(myNode.className).toBe('my-class');
});
notice that you can target myNode for testing using cpt.childNodes[0]
In order to improve #monastic-panic's answer, my two cents:
You don't have to create a class for that. Do it dynamically:
import createReactClass from 'create-react-class';
// need to be a class component
const Clazz = createReactClass({
render: () => {
return <YourFunctionalComponentName {...props} />;
},
});
ReactTestUtils.renderIntoDocument(<Clazz />);
Does the statics object work with ES6 classes in React?
class SomeComponent extends React.Component {
render() {
// ...
}
}
React.statics = {
someMethod: function() {
//...
}
};
Something like the above gives me undefined method someMethod when I do SomeComponent.someMethod()
statics only works with React.createClass. Simply declare the method as a static class method:
class SomeComponent extends React.Component {
static someMethod() {
//...
}
render() {
// ...
}
}
Regarding
React.statics = { ... }
You are literally creating a statics property on the React object. That property does not magically extend your component.
Although statics only works for React.createClass, you can still write static methods in ES6 notation. If you are using ES7, then you can also write static properties.
You can write statics inside ES6+ classes this way:
class Component extends React.Component {
static propTypes = {
...
}
static someMethod(){
}
}
Or outside the class like this:
class Component extends React.Component {
....
}
Component.propTypes = {...}
Component.someMethod = function(){....}
If you want to write it like the former, then you have to set stage: 0 on Babel (since its experimental).
Statics can be accessed without having to instantiate a component. Normally they aren't that useful but there are a few special cases. For example into routing when you leave the current page with doing a ACTION PERFORM then through Statics methods you can hold/ASKED the user whether he really want to leave the page.
For example:
exampleComponent= React.createClass({
statics:{
willTransitionFrom: function(transition,component){
// check any state here or value Aasked the user.
}
}
});
It exposes willTransitionTo and willTransitionFrom lifecycle methods. both are particular is useful as a static as you can actually cancel a transition before instantiating a component.
Stop. Just declare a class without extending React.Component and it will works.
class YourStaticClass {
static YourMethod(){
}
}
export default YourStaticClass;
in the other classes,
You can simply import your class and trigger the function
import YourStaticClass from "whatever";
...
YourStaticClass.YourMethod();
...
statics works only for React components, check docs.