note

Hooks, lazy, Suspense are not to be found here, I might update this at a later stage.

Styled components

To locally style components we cans use the "styled-components" library.

import styled from 'styled-components';

const MyButton = styled.button`
	color: pink;
	font-size: ${(props) => (props.big ? 3 : 2)}em;
	span {
		font-size: 2rem;
	}
`;

const Component = (
	<div>
		...
		<MyButton huge>
			Some content
			<span>bigger</span>
		</MyButton>
		...
	</div>
);

Styled-components support some theming. Define a const plain object like the following:

import style, { ThemeProvider, injectGlobal } from 'styled-components';

const theme = {
	text: 'black',
	bs: '0 12px 24px 0 rgba(0,0,0,0.8)',
	siteWidth: '1000px'
};

const StyledComponent = styled.div`
	max-width: ${(props) => props.theme.siteWidth};
`;

// to have generic style:
injectGlobals`
	html {
		box-sizing: border-box;
		font-size: 10px;
	}

	*, *:before, *:after* {
		box-sizing: inherit
	}

	body {
	   font-size: 16rem;
	}
`;

const App = (
	<ThemeProvider theme={theme}>
		<StyledComponent />
	</ThemeProvider>
);

The content below this point must be reviewed.

Prop-types

use prop-types

SayHello.propTypes = {
	firstName: PropTypes.string.isRequired,
	lastName: PropTypes.string.isRequired
};

Or as a static atribute of a class type of component definition.

production

use babel-plugin-transform-react-remove-prop-types

styling

Using inline styles may bring some issues, e.g. the handling of vendor prefixes, in that case have a look at to styling framework that may circumvent the problem: styled components, emotion or glamorous.

memory leak

Remember to remove or stop running function (e.g. setInterval) when a component is removed. Use componentWillUnmount to stop them.

DOM manipulation

To be able to manipulate the DOM element, e.g. to use another library, we need to add some code to the render function:

class Element extends React.Component {
	componentDidMount() {
		console.log(this.rootNode); // is ref to the nodeElement
	}
	render() {
		<div ref={(node) => (this.rootNode = node)}>...</div>;
	}
}

components exports

import React from 'react';
import connect from 'react-redux';

export class CartLine extends React.Component {
    ...
}

export default connect(() => {...})(Cartline);

This enables us to have the component that is simpler to test without redux store.

Context

Context is a technique to pass the context of a component to its children, whatever position they are in the hierarchy of the application.

This is usually done on high order components.

Image the following:

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
	document.getElementById('root'),
	<Container>
		<App />
	</Container>
);

the Container would defined as:

import react, {Component} from 'react'

class Container extends Component {

    static childContextTypes = {
        route: react.
Types.string,
        changeRoute: react.PropTypes.func
    }

    getChildContext() {
        return {
            route: this.state.route,
            changeRoute: this.handleChangeRoute
        }
    }

    ...

    render () {
        return <div>{this.props.children}</div>
    }
}

And in a component inside the Container exposing the context:

import react, { Component } from "react";

class MyComponent extends Component {
    static contextType: {
        route: react.PropTypes.string,
        changeRoute: react.PropTypes.func
    };

    render() {
        return <div>{this.context.route}</div>;
    }
}

Patterns

Compound components

ref: https://egghead.io/lessons/react-write-compound-components

On componement that has children components sharing state.

function ToggleOn({ on, children }) {
	return on ? children : null;
}
function ToggleOff({ on, children }) {
	return on ? null : children;
}
function ToggleButton({ on, toggle, ...props }) {
	return <Switch on={on} onClick={toggle} {...props} />;
}
class Toggle extends React.Component {
	static On = ToggleOn;
	static Off = ToggleOff;
	static Button = ToggleButton;
	static defaultProps = { onToggle: () => {} };
	state = { on: false };
	toggle = () =>
		this.setState(
			({ on }) => ({ on: !on }),
			() => this.props.onToggle(this.state.on)
		);
	render() {
		const children = React.Children.map(this.props.children, (child) =>
			React.cloneElement(child, {
				on: this.state.on,
				toggle: this.toggle
			})
		);
		return <div>{children}</div>;
	}
}

function App() {
	return (
		<Toggle onToggle={(on) => console.log('toggle', on)}>
			<Toggle.On>The button is on</Toggle.On>
			<Toggle.Off>The button is off</Toggle.Off>
			<Toggle.Button />
		</Toggle>
	);
}

Render becomes pretty unreadable (!).

High Order Component pattern

function withToggle(Component) {
	function Wrapper(props, context) {
		const toggleContext = context[TOGGLE_CONTEXT];
		return <Component {...toggleContext} {...props} />;
	}
	Wrapper.contextTypes = {
		[TOGGLE_CONTEXT]: PropTypes.object.isRequired
	};
	return Wrapper;
}

const MyToggle = withToggle(({ on, toggle }) => <button onClick={toggle}>{on ? 'on' : 'off'}</button>);

In the React dev tool, this brings an issue of name displayed for this withToggle compoments.

This can be corrected manually by adding a ${Component}.displayName = 'Component name'. Or, we can extract the inline component to a proper function and then it gets a name. The withXxxx would need to define its displayName in order to be findable (in the current context of use) in the dev-tool.

On HOC, it is not possible to add the ref prop as it applies to the staeless HOC factory. Therefore we must enhance the factory to forward the ref to the inner component. This is done using another prop (e.g. innerRef).

Another issue that may arise in the usage of HOC is the difficulty to reach static members that were defined on the inner component. hoist-non-react-statics was created to hoist those over tho the wrapper. It should be used in the wrapper `return hoistNonReactStatics(Wrapper, Component).

Final version looks like this:

function withtogle(Component) {
  function Wrapper(props, context) {
    const {innerRef, ..remainingProps} = props
    ...
  }
  Wrapper.displayName = `WithToggle(${Component.displayName ? Component.displayName : Component.name})`
  Wrapper.propTypes = { innerRef: PropTypes.func}
  Wrapper.WrappedComponent = Component
  return hoistNonReactStatics(Wrapper, Component)
}

"render" props pattern

Another technique to delegate the rendition of a component where it is used and only provide a Wrapper component is to use the "render" props Component.

class Toggle extends React.Component {
	static defaultProps = { onToggle: () => {} };
	state = { on: false };
	toggle = () =>
		this.setState(
			({ on }) => ({ on: !on }),
			() => this.props.onToggle(this.state.on)
		);
	render() {
		return this.props.render({
			on: this.state.on,
			toggle: this.toggle
		});
	}
}

<Toggle
	render={({ on, toggle }) => (
		<div>
			{on ? 'The button is on' : 'The button is off'}
			<Switch on={on} onClick={toggle} />
		</div>
	)}
/>;

Issue comes with the need of the user to define a onClick method. To support that use-case, we need to change this method to a getter and compose the passed props with the original ones from the wrapper.

Adding this to the Wrapper class:

getTogglerProps = ({ onClick, ...props } = {}) => {
	return {
		'aria-expanded': this.state.on,
		onClick: compose(onClick, this.toggle),
		...props
	};
};

compose being: const compose = (...fns) => (...args) => fns.forEach(fn => fn && fn(...args): the usage is then :

<button
	{...getTogglerProps({
		onClick: () => alert('hi'),
		id: 'hi'
	})}
>
	...
</button>

This will typically to use for className props.

Context Provider

Avoid having to pass props all over the place. Create a component XxxxxProvider and a ConnectedXxxx like this:

class ToggleProvider extends React.Component {
	static contextName = '__toggle__';
	static Renderer = class extends React.Component {
		static childContextTypes = {
			[ToggleProvider.contextName]: PropTypes.object.isRequired
		};
		getChildContext() {
			return {
				[ToggleProvider.contextName]: this.props.toggle
			};
		}
		render() {
			return this.props.children;
		}
	};
	render() {
		const { children, ...remainingProps } = this.props;
		return <Toggle {...remainingProps} render={(toggle) => <ToggleProvider.Renderer toggle={toggle} children={children} />} />;
	}
}

function ConnectedToggle(props, context) {
	return props.render(context[ToggleProvider.contextName]);
}
ConnectedToggle.contextTypes = {
	[ToggleProvider.contextName]: PropTypes.object.isRequired
};

Then wrap a Component with XxxxProvider and when a sub component need the props, wrap it with the ConnectedXxxxx.

Transpiling

Upgrading components to follow the API evolution

see the jscodeshift page

Redux

  • ducks ?

About me

Stuff I do with my brain, my fingers and an editor:

(Front-end) development - typescript, Vue, React, svelte(kit) web architectures web performance API design pragmatic SEO security Accessibility (A11y) front-end dev recruitment and probably more...

Feel free to , check out my open source projects, or just read the things I write on this site.