React Patterns in Angular Land
- Hayden Braxton
Kent C. Dodds
Isaac Mann
Inspiration
Hayden Braxton
JSLovers
Why Patterns?
Why React Patterns?
Overview
Overview
- Composition vs Inheritance
- Compound Components
- Render Prop
- State Reducer
- Bonus!
Composition vs Inheritance
Compound Compomnents
const RadioOption = props => {
return (
<div onClick={props.onClick}>
<RadioIcon isSelected={props.isSelected} />
{props.children}
</div>
);
};
class RadioGroup extends Component {
constructor(props) {
super(props);
this.state = {value: ""};
}
select = value => {
this.setState({ value }, () => {
this.props.onChange(this.state.value);
});
};
render() {
return (
<Context.Provider
value={{value: this.state.value, onSelect: this.select}}
>
{this.props.children}
</Context.Provider>
);
}
}
render() {
return (
<RadioGroup onChange={this.onChange}>
<Context.Consumer>
{({ value, onSelect }) => (
<RadioOption
value="am"
onClick={() => onSelect("am")}
isSelected={value === "am"}
>AM</RadioOption>
<RadioOption
value="fm"
onClick={() => onSelect("fm")}
isSelected={value === "fm"}
>FM</RadioOption>
)}
</Context.Consumer>
</RadioGroup>
)
}
Render Prop
class Mouse extends React.Component {
state = { x: 0, y: 0 }
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
})
}
render() {
return (
<div
style={{ height: '100%' }}
onMouseMove={this.handleMouseMove}
>
{this.props.children(this.state)}
</div>
)
}
}
const App = () => (
<div style={{ height: '100%' }}>
<Mouse>
{({ x, y }) => (
<h1>The mouse position is ({x}, {y})</h1>
)}
</Mouse>
</div>
)
@Component({
selector: 'mouse',
template: `
<div
style="height: 100%"
(mousemove)="handleMouseMove($event)"
>
<ng-container *ngTemplateOutlet="template; context: state">
</ng-container>
</div>
`,
})
export class Mouse {
@ContentChild(TemplateRef) template;
state = { x: 0, y: 0 }
handleMouseMove(event) {
this.state = {
x: event.clientX,
y: event.clientY
}
}
}
@Component({
selector: 'my-app',
template: `
<div style="height: 100%">
<mouse>
<ng-template let-x="x" let-y="y">
// The template gives us the state we need
// to render whatever we want here.
<h1>The mouse position is ({{x}}, {{y}})</h1>
</ng-template>
</mouse>
</div>
`,
})
export class AppComponent {}
State Reducer
class Toggle extends React.Component {
static defaultProps = {
stateReducer: (state, changes) => changes,
}
internalSetState(changes, callback) {
this.setState(state => {
const changesObject =
typeof changes === 'function' ? changes(state) : changes;
const reducedChanges =
this.props.stateReducer(state, changesObject) || {};
const {type: ignoredType, ...onlyChanges} = reducedChanges;
return Object.keys(onlyChanges).length ? onlyChanges : null;
}, callback);
}
toggle = ({type = Toggle.stateChangeTypes.toggle} = {}) =>
this.internalSetState(
({on}) => ({type, on: !on}),
() => this.props.onToggle(this.state.on),
)
getTogglerProps = ({onClick, ...props} = {}) => ({
onClick: callAll(onClick, () => this.toggle()),
'aria-pressed': this.state.on,
...props,
})
getStateAndHelpers() {
return {
on: this.state.on,
toggle: this.toggle,
}
}
render() {
return this.props.children(this.getStateAndHelpers())
}
}
class Usage extends React.Component {
state = {timesClicked: 0};
handleToggle = (...args) => {
this.setState(({timesClicked}) => ({
timesClicked: timesClicked + 1,
}))
}
handleReset = (...args) => {
this.setState({timesClicked: 0})
this.props.onReset(...args)
}
stateReducer = (state, changes) => {
if (this.state.timesClicked >= 4) {
return {...changes, on: false}
}
return changes
}
render() {
const {timesClicked} = this.state;
return (
<Toggle stateReducer={this.stateReducer} onToggle={this.handleToggle}>
{({on, toggle}) => (
<Switch on={on}/>
{timesClicked > 4 ? (Whoa, you clicked too much!)
: timesClicked > 0 ? (Click count: {timesClicked})
: null}
)}
</Toggle>
)
}
}
Bonus!
Angular Context API
Angular Props Directive
Thanks!
Resources
React Patterns in Angular Land
By haydenbraxton
React Patterns in Angular Land
React and Angular are the most widely used front-end libraries today. Both tools have undoubtedly enabled developers to become more productive, and both have seen a lot of maturation in the past couple years. Rather than pitting the two against each other in a high-stakes death match, perhaps there exist opportunities for developers of one library to learn from the best practices of the other. In this talk, though perhaps not as exciting as a bloody cage fight, we’ll learn about the most popular patterns circulating in the React community and see how we can apply the same techniques to write better Angular code.
- 700