React Fundamentals
(For People That Already Know React)
Did you know?
JSX is just an abstraction over function calls. In much the same way that this:
const foo = {...bar};
Gets transpiled to this:
(function () {
var foo = Object.assign({}, bar);
}());
Did you know?
When you write JSX markup, it also gets converted to other code, so that this:
const foo = <Foo />;
Gets transpiled to this:
(function () {
var foo = React.createElement(Foo, null);
}());
Why is this important?
This means that JSX is nothing more than a set of nested function calls, and any patterns that can be employed with standard function calls can also be employed within JSX layouts.
Why is this important?
The 'children' prop is special within React development, in that it's typically used for passing subcomponents to a component for rendering. You don't have to pass subcomponent information this way, though, and it's not even required to be React elements. The only requirement is that a component returns React elements, a string, false, or null.
Typical React Children
render () {
return (
<div>Hello World!</div>
);
}
Less Typical React Children
render () {
return (
<div children="Hello World!" />
);
}
Less Typical React Children
render () {
const props = {
children: "Hello World",
};
return (
<div {...props} />
);
}
Whaaaaaa?!
render () {
const { name } = this.props;
return (
<Helloenator>
{({ greeting }) => `${greeting} ${name}!`}
</Helloenator>
);
}
Render Callbacks
Remember that component chunks in JSX only need to return React elements, strings, false, or null. This is why we're able to do things like this:
render () {
return (
{this.props.someFlag && (
<div>Hello World!</div>
)}
);
}
Render Callbacks
Remember that component chunks in JSX only need to return React elements, strings, false, or null. This is why we're able to do things like this:
render () {
return (
{this.props.someFlag && (
<div>Hello World!</div>
)}
); // returns false when !someFlag
}
Render Callbacks
It's also the reason we're able to do ternary operations like this:
render () {
return (
{this.props.someFlag ? (
<div>Hello World!</div>
) : null}
); // returns null when !someFlag
}
Render Callbacks
And it's also the reason we're able to do what are called render callbacks, like this:
render () {
const { name } = this.props;
return (
<Helloenator>
{({ greeting }) => `${greeting} ${name}!`}
</Helloenator>
); // returns the computed string
}
Render Callbacks
Components have to be written with awareness of render callback functions, like this:
render () {
return (
<div>
{this.props.children({
greeting: 'Hello'
})}
</div>
);
}
Render Callbacks
You don't even need to use 'children' at all, you can use any prop in exactly the same way:
render () {
return (
<div>
{this.props.render({
greeting: 'Hello'
})}
</div>
);
}
Render Callbacks
And then consume your component with a self-closing tag, since you don't need children:
render () {
const { name } = this.props;
return (
<Helloenator render={({ greeting }) => (
`${greeting} ${name}`
)} />
);
}
Non-UI Components
Since we can simply return null from any React component, that makes them great for creating declarative wrappers around tradition imperative code implementations.
This is the reason the bv-loader-internal-components repo has components subdivided into Analytics, Service, and UI component directories.
Non-UI Components
const LocalStorage = class extends React.Component {
_getItem () {
return localStorage.getItem(this.props.name);
}
render () {
let value;
try {
value = this._getItem()
} catch (error) {
value = error;
}
return (value instanceof Error ? null : this.props.children({ value }));
}
}
Non-UI Components
import Helloenator from './helloenator';
import LocalStorage from './local-storage';
const LocalStorageDrivenView = class extends React.Component {
render () {
return (
<LocalStorage name="FirstName}>
{({ value }) => (
<Helloenator>
{({ greeting }) => `${greeting} ${value}`}
</Helloenator>
)}
</LocalStorage>
);
}
}
Functional Paradigm
Since JSX is just nested function calls, you can maybe better envision the previous example in functional terms, like this:
const localStorageDrivenView = name => {
const value = localStorage(name);
const greeting = helloenator();
return `${greeting} ${value}`;
};
Higher Order Components
If you find yourself performing the same set of operations across multiple components, you may have opportunity to pull out that repeated functionality, and create a higher-order component that can do those operations and apply them to your component in the form of props.
A higher-order component can be thought of as a curried (or partial) function, which is just waiting for you to provide the rest of its parameters.
Higher Order Components
const withContext = ctx => Component => {
const ContextWrapperComponent = class extends React.Component {
static contextTypes = ctx;
render () {
return (
<Component _ctx={this.context} {...this.props} />
);
}
};
return ContextWrapperComponent;
};
Higher Order Components
const ContextualComponent = ({ _ctx: { bar }, foo }) => (
<div>
<div>Foo from props = ${foo}</div>
<div>Bar from context = ${bar}</div>
</div>
);
export default withContext({
bar: propTypes.string,
})(ContextualComponent);
Functional Paradigm
const withContext = ctx => Component => {
const context = getContextValues(ctx);
return ContextWrapperComponent({
children: Component({
_ctx: context,
...this.props,
}),
});
};
I AM REACT!
AND SO CAN YOU!
fn.
React Fundamentals
By Richard Lindsey
React Fundamentals
- 179