I just started to learn ReactJS and done some tutorials. Have noticed that some write function and others do not. Some examples below. What is the difference? What should I use and when?
Render
With function
var $class$ = React.createClass({
render: function() {
return (
<div />
);
}
});
Without function
const $class$ = React.createClass({
render() {
return (
<div />
);
}
});
Update
With function
componentDidUpdate: function(prevProps, prevState) {
$END$
},
Without function
componentDidUpdate(prevProps, prevState) {
$END$
},
Default Props
With function
getDefaultProps: function() {
return {
$END$
};
},
Without function
getDefaultProps() {
return {
$END$
};
},
Those without the function keyword are the result of using the new shorter ES6 method definitions.
You can read more here: Method Definitions - JavaScript | MDN
As far as I am aware, there is no notable difference in behaviour between a shorthand definition and including the function keyword other than the former having reduced support across environments.
Related
I saw below code somewhere and I'm curious. It looks cleaned but unusual to me. Why state = {} is declared without an constructor?
and load declared without a function keyword? As I know for there are ways to write a function
function async load() {}
or
const async load = ()=>{}
And what ...args does? is it spread arguments?
import View from './View';
import loadData from './loadData';
export default class extends Component {
state = {};
load = this.load.bind(this);
async load(...args) {
try {
this.setState({ loading: true, error: false });
const data = await loadData(...args);
this.setState({ loading: false, data });
} catch (ex) {
this.setState({ loading: false, error: true });
}
}
render() {
return (
<View {...this.props} {...this.state} onLoad={this.load} />
);
}
}
The state = {} declaration is a class property, which is currently not part of the JavaScript language. Certain utilities such as Babel will compile this into legal JavaScript code.
However, the lack of a function keyword within classes, as well as the ... operator are part of ECMAScript 6, which has been officially implemented into the language (though some browsers do not recognize it yet).
Class Definition
Spread Operator
Yes, you can initialize state without a constructor for a React class component:
class Counter extends Component {
state = { value: 0 };
handleIncrement = () => {
this.setState(prevState => ({
value: prevState.value + 1
}));
};
handleDecrement = () => {
this.setState(prevState => ({
value: prevState.value - 1
}));
};
render() {
return (
<div>
{this.state.value}
<button onClick={this.handleIncrement}>+</button>
<button onClick={this.handleDecrement}>-</button>
</div>
)
}
}
It uses class field declarations which are not part of the language yet but enabled with Babel. You can checkout a sample application over here.
You can also use state in React function components (without a constructor), but using higher-order components or render prop components. You can find out more about it here.
When I am mapping through props in a components render method, can I pass in prop values to a function. I have forgotten the correct way to do this. For instance I have a category tag control which just lists some categories with a name, however onClick I want to perform some functionality for the individual category.
var React = require("react");
var CategoryTags = React.createClass({
propTypes: {
categories: React.PropTypes.array.isRequired
},
render: function() {
var categoryRows = this.props.categories.map(function (c, i) {
return (
<button onClick={ this.linkToCategory.bind(name) } >
{c.name}
</button>
);
}.bind(this));
return (
<span>{categoryRows}</span>
);
},
linkToCategory: function (c) {
console.log(c);
}
});
So in this example, while mapping though the categories, I want to pass in the name of the individual category so I can parse the link. Of course it would make sense for the object to have a link property but it didn't in this case.
Example of the categories object being passed in to the component props
categories = [{'name': 'cat1'}, {'name': 'cat2'}];
Binding a function inside .map is not a good idea, because you are always going to pass a new function to the component, even if no other prop changed. That means it will always be re-rendered, even if it doesn't have to be.
But anyways, the problem with your code is that
There is no variable name.
You are using .bind incorrectly.
The call should be
this.linkToCategory.bind(this, c.name)
The first argument passed to bind .bind is the value of this, not the first argument (which you should know since you are using .bind(this) as well).
You can learn more about .bind in the MDN documentation.
You can also return a function from your event handler:
var CategoryTags = React.createClass({
propTypes: {
categories: React.PropTypes.array.isRequired
},
render: function() {
var categoryRows = this.props.categories.map(function (c, i) {
return (
<button onClick={this.linkToCategory(c)} >
{c.name}
</button>
);
}.bind(this));
return (
<span>{categoryRows}</span>
);
},
linkToCategory: function (c) {
return () => {
console.log(c);
}
}
});
I have the next component 'Father' that contains a 'Children' component in React js.
var Father = React.createClass({
render: function () {
return (
<div>
<Children/>
</div>
);
},
onUpdate: function(state) {
this.setState(state);
} });
I want to call the onUpdate function on the father from the children BUT without calling the 'Children' method 'componentDidUpdate' because I'm using that method for some other thing that breaks my application.
How can I do that?
Pass it down in properties. If you need to update only specific parts and prevent your children from updating, use the method shouldComponentUpdate
var Father = React.createClass({
render: function () {
return (
<div>
<Children onUpdateCallback={this.onUpdate}/>
</div>
);
},
onUpdate: function(state) {
this.setState(state);
}
});
var Child = React.createClass({
render: function () { ... },
shouldComponentUpdate: function (prevProps, prevState) { ... return false}
});
If your Children can't/shouldn't update while the Parent does, I think you are probably doing something wrong, but good luck.
Since React doesn't have any builtin way to manage document.title, I used to set it inside componentDidMount of my route handlers.
However now I need to amend the title based on state fetched asynchronously. I started putting assingments into componentDidUpdate, but every now and then I forget to put document.title assignment into some pages, and previous title sticks around until I finally notice it.
Ideally I'd like a way to express document.title declaratively, without having to assign it. Some kind of “fake” component would probably be most convenient, given that I want to be able to specify the document title at several nesting levels:
On top level (the default title);
On page level (for some of the pages, but not all);
Sometimes, on inner component level (e.g. user typing into a field).
Additional requirements:
Title specified in child should override title specified by parent;
Reliable (guarantees cleanup on route change);
Should not emit any DOM (i.e. no hacks with component returning <noscript>);
I'm using react-router but it's better if this component works with other routers too.
Anything I can use?
I wrote react-document-title just for that.
It provides a declarative way to specify document.title in a single-page app.
If you want to get title on server after rendering components to string, call DocumentTitle.rewind().
Features
Does not emit DOM, not even a <noscript>;
Like a normal React compoment, can use its parent's props and state;
Can be defined in many places throughout the application;
Supports arbitrary levels of nesting, so you can define app-wide and page-specific titles;
Works on client and server.
Example
Assuming you use something like react-router:
var App = React.createClass({
render: function () {
// Use "My Web App" if no child overrides this
return (
<DocumentTitle title='My Web App'>
<this.props.activeRouteHandler />
</DocumentTitle>
);
}
});
var HomePage = React.createClass({
render: function () {
// Use "Home" while this component is mounted
return (
<DocumentTitle title='Home'>
<h1>Home, sweet home.</h1>
</DocumentTitle>
);
}
});
var NewArticlePage = React.createClass({
mixins: [LinkStateMixin],
render: function () {
// Update using value from state while this component is mounted
return (
<DocumentTitle title={this.state.title || 'Untitled'}>
<div>
<h1>New Article</h1>
<input valueLink={this.linkState('title')} />
</div>
</DocumentTitle>
);
}
});
Source
I keep track of mounted instances and only use title given to the top DocumentTitle in the mounted instance stack whenever it updates, gets mounted or unmounted. On server, componentWillMount fires but we won't get didMount or willUnmount, so we introduce DocumentTitle.rewind() that returns a string and destroys state to prepare for next request.
var DocumentTitle = React.createClass({
propTypes: {
title: PropTypes.string
},
statics: {
mountedInstances: [],
rewind: function () {
var activeInstance = DocumentTitle.getActiveInstance();
DocumentTitle.mountedInstances.splice(0);
if (activeInstance) {
return activeInstance.props.title;
}
},
getActiveInstance: function () {
var length = DocumentTitle.mountedInstances.length;
if (length > 0) {
return DocumentTitle.mountedInstances[length - 1];
}
},
updateDocumentTitle: function () {
if (typeof document === 'undefined') {
return;
}
var activeInstance = DocumentTitle.getActiveInstance();
if (activeInstance) {
document.title = activeInstance.props.title;
}
}
},
getDefaultProps: function () {
return {
title: ''
};
},
isActive: function () {
return this === DocumentTitle.getActiveInstance();
},
componentWillMount: function () {
DocumentTitle.mountedInstances.push(this);
DocumentTitle.updateDocumentTitle();
},
componentDidUpdate: function (prevProps) {
if (this.isActive() && prevProps.title !== this.props.title) {
DocumentTitle.updateDocumentTitle();
}
},
componentWillUnmount: function () {
var index = DocumentTitle.mountedInstances.indexOf(this);
DocumentTitle.mountedInstances.splice(index, 1);
DocumentTitle.updateDocumentTitle();
},
render: function () {
if (this.props.children) {
return Children.only(this.props.children);
} else {
return null;
}
}
});
module.exports = DocumentTitle;
Take a look at the NFL's react-helmet.
class Layout extends React.Component {
constructor(props){
super(props);
document.title = this.props.title;
}
render(){
return(
<div>
</div>
);
}
}
and then <Layout title="My Title"/> that easy!
Try react-frozenhead, it's actually more sophisticated than react-document-title - it allows us change title, description and anything else in section.
Meanwhile, 3 years have gone! ;-)
If you want to manipulate other page headers than title (like description, canonical, etc.), react-document-meta NPM dependency could be a good thing to use.
I'm working with React and using React-Bootstrap components.
I found some issues in the React-Bootstrap library which I "fixed" (or "workarounded") by editing the react-bootstrap.js file. The problem is that if tomorrow a new version of react-bootstrap comes out, then I will have to copy-paste/re-write/whatever all the code I wrote in the react-bootstrap.js file to the new one. I don't want to do this, so I'm wandering if there is a way to modify the component classes (i.e. change the render function) provided by react-bootstrap without touching the react-bootstrap.js file. The problem is that I can't figure out how to do this, or at least I'm not finding easy to understand the inner working of the component classes. Any ideas on how could I accomplish this?
Thanks in advance!
You could use a wrapping component, that overrides methods of the original component after it's mounted:
function wrapComponent (originalComponent, override) {
return React.createClass({
componentDidMount: function () {
for (var property in override) {
if (override.hasOwnProperty(property)) {
this.refs.original[property] = override[property];
}
}
},
render: function () {
return this.transferPropsTo(
<originalComponent ref="original">{ this.props.children }</originalComponent>
);
}
});
}
var ConsoleSample = React.createClass({
// This method can still be used:
prefix: function (text) {
return "prefix: " + text;
},
// This method will be overridden:
output: function (text) {
console.log(this.prefix(text));
},
onClick: function () {
this.output("Hello world");
},
render: function () {
return <button onClick={this.onClick}>{ this.props.children }</button>
}
});
var Application = React.createClass({
render: function () {
var AlertSample = wrapComponent(ConsoleSample, {
output: function (text) {
alert(this.prefix(text));
}
});
return <div>
<ConsoleSample>This should console.log</ConsoleSample>
<AlertSample>This should alert</AlertSample>
</div>
}
});
React.renderComponent(<Application />, document.body.lastChild);
It's a simple hack though. I'd agree that the correct solution is to fork React-Bootstrap.