class Product Row extends Component should Component Updatenext




























































![const enhance = compose( with. State('products', 'set. Products', []), with. State('loading', 'set. Loading', false), const enhance = compose( with. State('products', 'set. Products', []), with. State('loading', 'set. Loading', false),](https://slidetodoc.com/presentation_image/d5421210fc8a0bd45fecdaa9e29664c4/image-61.jpg)
![const enhance = compose( with. State('products', 'set. Products', []), with. State('loading', 'set. Loading', false), const enhance = compose( with. State('products', 'set. Products', []), with. State('loading', 'set. Loading', false),](https://slidetodoc.com/presentation_image/d5421210fc8a0bd45fecdaa9e29664c4/image-62.jpg)
![const enhance = compose( with. State('products', 'set. Products', []), with. State('loading', 'set. Loading', false), const enhance = compose( with. State('products', 'set. Products', []), with. State('loading', 'set. Loading', false),](https://slidetodoc.com/presentation_image/d5421210fc8a0bd45fecdaa9e29664c4/image-63.jpg)






























- Slides: 93
Перерисовка компонентов class Product. Row extends Component { should. Component. Update(next. Props) { return next. Props. id !== this. props. id } render() { //. . . } } 10
Компонент высшего порядка (HOC) const Name = () => <p>Guest</p> const with. Greeting = Wrapped. Component => props => ( <div> Hi, <Wrapped. Component {. . . props} /> </div> ) with. Greeting(Name) 12
Компонент высшего порядка (HOC) const Name = () => <p>Guest</p> const with. Greeting = Wrapped. Component => props => ( <div> Hi, <Wrapped. Component {. . . props} /> </div> ) with. Greeting(Name) 13
Компонент высшего порядка (HOC) const Name = () => <p>Guest</p> const with. Greeting = Wrapped. Component => props => ( <div> Hi, <Wrapped. Component {. . . props} /> </div> ) with. Greeting(Name) 14
Компонент высшего порядка (HOC) const Name = () => <p>Guest</p> const with. Greeting = Wrapped. Component => props => ( <div> Hi, <Wrapped. Component {. . . props} /> </div> ) with. Greeting(Name) 15
only. Update. For. Keys const Product. Row = only. Update. For. Keys(['id)([' }) id, name, price /*. . . больше свойств) <= ({ /* > tr< > td>{name}</td< > td>{price}</td< > Link to={`/products/${id}`}>Details</Link< /> td< /> tr< ( ( 16
only. Update. For. Keys const enhance = only. Update. For. Keys(['id([' const Product. Row = ({ id, name, price /*. . . больше свойств) <= ({ /* > tr< > td>{name}</td< > td>{price}</td< > Link to={`/products/${id}`}>Details</Link< /> td< /> tr< ( export default enhance(Product. Row( 17
only. Update. For. Keys const only. Update. For. Keys = prop. Keys => Wrapped. Component => class extends Component { should. Component. Update(next. Props) { return prop. Keys. some(key => this. props[key] !== next. Props[key] ) } render() { return <Wrapped. Component {. . . this. props} /> } } 18
only. Update. For. Keys const only. Update. For. Keys = prop. Keys => Wrapped. Component => class extends Component { should. Component. Update(next. Props) { return prop. Keys. some(key => this. props[key] !== next. Props[key] ) } render() { return <Wrapped. Component {. . . this. props} /> } } 19
only. Update. For. Keys const only. Update. For. Keys = prop. Keys => Wrapped. Component => class extends Component { should. Component. Update(next. Props) { return prop. Keys. some(key => this. props[key] !== next. Props[key] ) } render() { return <Wrapped. Component {. . . this. props} /> } } 20
only. Update. For. Keys const only. Update. For. Keys = prop. Keys => Wrapped. Component => class extends Component { should. Component. Update(next. Props) { return prop. Keys. some(key => this. props[key] !== next. Props[key] ) } render() { return <Wrapped. Component {. . . this. props} /> } } 21
only. Update. For. Keys const only. Update. For. Keys = prop. Keys => Wrapped. Component => class extends Component { should. Component. Update(next. Props) { return prop. Keys. some(key => this. props[key] !== next. Props[key] ) } render() { return <Wrapped. Component {. . . this. props} /> } } 22
Граничные условия в render const Product. List = ({ products) <= ({ > table< > thead< > tr< > th>Name</th< > th>Price</th< /> tr< /> thead< > tbody< } products. map(p <= >Product. Row key={p. id} {. . . p{(</ { /> tbody< /> table< ( 24
Граничные условия в render const Product. List = ({ products} <= ({ if (!products. length} ( return <No. Data /> { return) > table< */} та же таблица{/* /> table< ( { 25
Граничные условия в render const Product. List = ({ loading, products} <= ({ if (loading} ( return <Spinner</ { if (!products. length} ( return <No. Data</ { return (. . . ( { 26
Граничные условия в render const Product. List = ({ current. User, loading, products} <= ({ if (!current. User} ( return <Not. Authorized</ { if (loading} ( return <Spinner</ { if (!products. length} ( return <No. Data</ { return (. . . ( { 27
Граничные условия в render const enhance = branch( ({ products }) => !products. length, () => null ) const Product. List = ({ products }) => (. . . ) export default enhance(Product. List) 28
Граничные условия в render const enhance = branch( ({ products }) => !products. length, () => null ) const Product. List = ({ products }) => (. . . ) export default enhance(Product. List) 29
Граничные условия в render const enhance = branch( ({ products }) => !products. length, () => null ) const Product. List = ({ products }) => (. . . ) export default enhance(Product. List) 30
Граничные условия в render const render. Nothing = () => null const enhance = branch( ({ products }) => !products. length, render. Nothing ) const Product. List = ({ products }) => (. . . ) export default enhance(Product. List) 31
branch const branch = (cond, cb) => Wrapped. Component => props => cond(props) ? cb(props) : <Wrapped. Component {. . . props} /> 32
branch const branch = (cond, cb) => Wrapped. Component => props => cond(props) ? cb(props) : <Wrapped. Component {. . . props} /> 33
Подготовка данных для отрисовки const formatted. Price = price => `$${price}` const enhance = only. Update. For. Keys(['id']) const Product. Row = ({ id, name, price }) => ( <tr> <td>{name}</td> <td>{formatted. Price(price)}</td> <Link to={`/products/${id}`}>Details</Link> </td> </tr> ) export default enhance(Product. Row) 35
Подготовка данных для отрисовки const formatted. Price = price => `$${price}` const enhance = only. Update. For. Keys(['id'])( with. Props(({ price }) => ({ price: formatted. Price(price) })) ) const Product. Row = ({ id, name, price }) => (. . . ) export default enhance(Product. Row) 36
with. Props const with. Props = mapper => Wrapped. Component => props => { const new. Props = {. . . props, . . . mapper(props) } return <Wrapped. Component {. . . new. Props} /> } 37
with. Props const with. Props = mapper => Wrapped. Component => props => { const new. Props = {. . . props, . . . mapper(props) } return <Wrapped. Component {. . . new. Props} /> } 38
Композиция with. Router( inject. Intl( connect(map. State. To. Props, map. Dispatch. To. Props) ) )(Landing. Page) 39
Композиция const enhance = compose( with. Router, inject. Intl, connect(map. State. To. Props, map. Dispatch. To. Props) ) enhance(Landing. Page) 40
compose const compose = (. . . hocs) => Wrapped. Component => hocs. reduce((acc, hoc) => hoc(acc), Wrapped. Component) 41
compose const compose = (. . . hocs) => Wrapped. Component => hocs. reduce((acc, hoc) => hoc(acc), Wrapped. Component) 42
compose const compose = (. . . hocs) => Wrapped. Component => hocs. reduce((acc, hoc) => hoc(acc), Wrapped. Component) 43
with. Props const formatted. Price = price => `$${price}` const enhance = only. Update. For. Keys(['id'])( with. Props(({ price }) => ({ price: formatted. Price(price) })) ) const Product. Row = ({ id, name, price }) => (. . . ) export default enhance(Product. Row) 44
with. Props const formatted. Price = price => `$${price}` const enhance = compose( only. Update. For. Keys(['id']), with. Props(({ price }) => ({ price: formatted. Price(price) })) ) const Product. Row = ({ id, name, price }) => (. . . ) export default enhance(Product. Row) 45
recompose • • https: //github. com/acdlite/recompose 15. 1 k. B minified, 4 k. B gzipped, � 9700 есть все функции, которые мы реализовали и множество других хорошая поддержка �flow �Rx. JS �Type. Script �React Native 46
with. Handlers const Add. To. Cart. Button = ({ price, product. Id, on. Add. To. Cart }) => ( <div> <span>{price}</span> <button on. Click={() => on. Add. To. Cart(product. Id)}> Add to cart </button> </div> ) 48
with. Handlers const Add. To. Cart. Button = ({ price, product. Id, on. Add. To. Cart }) => ( <div> <span>{price}</span> <button on. Click={() => on. Add. To. Cart(product. Id)}> Add to cart </button> </div> ) 49
with. Handlers const Add. To. Cart. Button = ({ price, product. Id, on. Add. To. Cart }) => ( <div> <span>{price}</span> <button on. Click={() => on. Add. To. Cart(product. Id)}> Add to cart </button> </div> ) 50
with. Handlers const enhance = with. Handlers({ on. Click. Handler: ({ on. Add. To. Cart, product. Id }) => () => on. Add. To. Cart(product. Id) }) const Add. To. Cart. Button = ({ price, on. Click. Handler }) => ( <div> <span>{price}</span> <button on. Click={on. Click. Handler}>Add to cart</button> </div> ) export default enhance(Add. To. Cart. Button) 51
with. Handlers const enhance = with. Handlers({ on. Click. Handler: ({ on. Add. To. Cart, product. Id }) => () => on. Add. To. Cart(product. Id) }) const Add. To. Cart. Button = ({ price, on. Click. Handler }) => ( <div> <span>{price}</span> <button on. Click={on. Click. Handler}>Add to cart</button> </div> ) export default enhance(Add. To. Cart. Button) 52
with. Handlers const enhance = with. Handlers({ on. Click. Handler: ({ on. Add. To. Cart, product. Id }) => () => on. Add. To. Cart(product. Id) }) const Add. To. Cart. Button = ({ price, on. Click. Handler }) => ( <div> <span>{price}</span> <button on. Click={on. Click. Handler}>Add to cart</button> </div> ) export default enhance(Add. To. Cart. Button) 53
with. Handlers const enhance = with. Handlers({ on. Click. Handler: ({ on. Add. To. Cart, product. Id }) => () => on. Add. To. Cart(product. Id) }) const Add. To. Cart. Button = ({ price, on. Click. Handler }) => ( <div> <span>{price}</span> <button on. Click={on. Click. Handler}>Add to cart</button> </div> ) export default enhance(Add. To. Cart. Button) 54
Загрузка данных class Product. List. Container extends Component { constructor(props) { /* инициализируем cтейт */ } load. Products = () => { /* идем на сервер за данными */ } component. Will. Mount() { this. load. Products() } render() { /* рисуем */ } } 56
Загрузка данных class Product. List. Container extends Component { constructor(props) { super(props) this. state = { products: [], loading: false } } //. . . } 57
Загрузка данных class Product. List. Container extends Component { load. Products = () => { this. set. State({ loading: true }) fetch. Products(). then(products => this. set. State({ products, loading: false }) ) } //. . . } 58
Загрузка данных class Product. List. Container extends Component { render() { return ( <Product. List products={this. state. products} loading={this. state. loading} /> ) } //. . . } 59
class Product. List. Container extends Component { constructor(props) { super(props) this. state = { products: [], loading: false } } load. Products = () => { this. set. State({ loading: true }) fetch. Products(). then(products => this. set. State({ products, loading: false }) ) } component. Will. Mount() { this. load. Products() } render() { const { products, loading } = this. state return (<Product. List products={products} loading={loading} />) } } 60
const enhance = compose( with. State('products', 'set. Products', []), with. State('loading', 'set. Loading', false), with. Handlers({ load. Products: ({ set. Loading, set. Products }) => () => { set. Loading(true) fetch. Products(). then(products => { set. Loading(false) set. Products(products) }) } }), lifecycle({ component. Will. Mount() { this. props. load. Products() } }) ) export default enhance(Product. List) 61
const enhance = compose( with. State('products', 'set. Products', []), with. State('loading', 'set. Loading', false), with. Handlers({ load. Products: ({ set. Loading, set. Products }) => () => { set. Loading(true) fetch. Products(). then(products => { set. Loading(false) set. Products(products) }) } }), lifecycle({ component. Will. Mount() { this. props. load. Products() } }) ) export default enhance(Product. List) 62
const enhance = compose( with. State('products', 'set. Products', []), with. State('loading', 'set. Loading', false), with. Handlers({ load. Products: ({ set. Loading, set. Products }) => () => { set. Loading(true) fetch. Products(). then(products => { set. Loading(false) set. Products(products) }) } }), lifecycle({ component. Will. Mount() { this. props. load. Products() } }) ) export default enhance(Product. List) 63
component. From. Prop <Navbar> <Navbar. Item to={home. Path}>Home</Navbar. Item> <Navbar. Item to={cart. Path}>Cart</Navbar. Item> <Navbar. Item on. Click={sign. Out}>Sign Out</Navbar. Item> </Navbar> 65
component. From. Prop const Navbar. Item = ({ to, on. Click, children }) => { const class. Name = 'navbar-item' if (to) { return ( <Link class. Name={class. Name} to={to}>{children}</Link> ) } if (on. Click) { return ( <button class. Name={class. Name} on. Click={on. Click}>{children}</button> ) } return null } 66
component. From. Prop const enhance = compose( with. Props({ class. Name: 'navbar-item' }), branch( ({ to }) => to, with. Props({ component: Link }) ), branch( ({ on. Click }) => on. Click, with. Props({ component: 'button' }) ) ) export default enhance(component. From. Prop('component')) 67
component. From. Prop const enhance = compose( with. Props({ class. Name: 'navbar-item' }), branch( ({ to }) => to, with. Props({ component: Link }) ), branch( ({ on. Click }) => on. Click, with. Props({ component: 'button' }) ) ) export default enhance(component. From. Prop('component')) 68
const map. State. To. Props = (state, { match: { params: { id } } }) => ({ product: get. Product(state, id) }) class Product. Profile. Container extends Component { should. Component. Update(next. Props) { return next. Props. match. params. id !== this. props. match. params. id } component. Did. Mount() { request. Product(this. props. match. params. id) } render() { return <Product. Profile product={this. props. product} /> } } export default connect(map. State. To. Props, map. Dispatch. To. Props)(Product. Profile. Container) 70
const map. State. To. Props = (state, { match: { params: { id } } }) => ({ product: get. Product(state, id) }) class Product. Profile. Container extends Component { should. Component. Update(next. Props) { return next. Props. match. params. id !== this. props. match. params. id } component. Did. Mount() { request. Product(this. props. match. params. id) } render() { return <Product. Profile product={this. props. product} /> } } export default connect(map. State. To. Props, map. Dispatch. To. Props)(Product. Profile. Container) 71
const map. State. To. Props = (state, { match: { params: { id } } }) => ({ product: get. Product(state, id) }) class Product. Profile. Container extends Component { should. Component. Update(next. Props) { return next. Props. match. params. id !== this. props. match. params. id } component. Did. Mount() { request. Product(this. props. match. params. id) } render() { return <Product. Profile product={this. props. product} /> } } export default connect(map. State. To. Props, map. Dispatch. To. Props)(Product. Profile. Container) 72
const map. State. To. Props = (state, { match: { params: { id } } }) => ({ product: get. Product(state, id) }) class Product. Profile. Container extends Component { should. Component. Update(next. Props) { return next. Props. match. params. id !== this. props. match. params. id } component. Did. Mount() { request. Product(this. props. match. params. id) } render() { return <Product. Profile product={this. props. product} /> } } export default connect(map. State. To. Props, map. Dispatch. To. Props)(Product. Profile. Container) 73
const map. State. To. Props = (state, { match: { params: { id } } }) => ({ id, product: get. Product(state, id) }) class Product. Profile. Container extends Component { should. Component. Update(next. Props) { return next. Props. match. params. id !== this. props. id } component. Did. Mount() { request. Product(this. props. id) } render() { return <Product. Profile product={this. props. product} /> } } export default connect(map. State. To. Props, map. Dispatch. To. Props)(Product. Profile. Container) 74
const map. State. To. Props = (state, { match: { params: { id } } }) => ({ id, product: get. Product(state, id) }) class Product. Profile. Container extends Component { should. Component. Update(next. Props) { return next. Props. match. params. id !== this. props. id } component. Did. Mount() { request. Product(this. props. id) } render() { return <Product. Profile product={this. props. product} /> } } export default connect(map. State. To. Props, map. Dispatch. To. Props)(Product. Profile. Container) 75
const map. State. To. Props = (state, { match: { params: { id } } }) => ({ id, product: get. Product(state, id) }) class Product. Profile. Container extends Component { should. Component. Update(next. Props) { return next. Props. match. params. id !== this. props. id } component. Did. Mount() { request. Product(this. props. id) } render() { return <Product. Profile product={this. props. product} /> } } export default connect(map. State. To. Props, map. Dispatch. To. Props)(Product. Profile. Container) 76
const map. State. To. Props = (state, { match: { params: { id } } }) => ({ id, product: get. Product(state, id) }) const enhance = compose( only. Update. For. Prop. Types, set. Prop. Types({ product: Product. Shape }), connect(map. State. To. Props, map. Dispatch. To. Props), lifecycle({ component. Did. Mount() { request. Product(this. props. id) } }) ) export default enhance(Product. Profile) 77
const map. State. To. Props = (state, { match: { params: { id } } }) => ({ id, product: get. Product(state, id) }) const enhance = compose( only. Update. For. Prop. Types, set. Prop. Types({ product: Product. Shape }), connect(map. State. To. Props, map. Dispatch. To. Props), lifecycle({ component. Did. Mount() { request. Product(this. props. id) } }) ) export default enhance(Product. Profile) 78
const map. State. To. Props = (state, { match: { params: { id } } }) => ({ id, product: get. Product(state, id) }) const enhance = compose( only. Update. For. Prop. Types, set. Prop. Types({ product: Product. Shape }), connect(map. State. To. Props, map. Dispatch. To. Props), lifecycle({ component. Did. Mount() { request. Product(this. props. id) } }) ) export default enhance(Product. Profile) 79
Альтернативы recompose • написать руками? �� • Recompact 38. 2 k. B minified, 6. 9 k. B gzipped, � 260 (https: //github. com/neoziro/recompact) • Reassemble 21. 7 k. B minified, 4. 5 k. B gzipped, � 54 (https: //github. com/wikiwi/reassemble) 80
Бенчмарки 100 HOCs, измеряем число обновлений в секунду https: //github. com/neoziro/recompact/tree/master/src/__benchmarks__ 81
Тестирование const with. Product. Count = with. Props( ({ products }) => ({ count: products. length }) ) describe('with. Product. Count decorator', () => { const products = [{ id: 1 }] const Mock. Component = () => <div /> it('adds product count prop', () => { const Enhanced = with. Product. Count(Mock. Component) const wrapper = mount(<Enhanced products={products} />) const subject = wrapper. find(Mock. Component) expect(subject. prop('count')). to. Be(1) }) }) 83
Тестирование const with. Product. Count = with. Props( ({ products }) => ({ count: products. length }) ) describe('with. Product. Count decorator', () => { const products = [{ id: 1 }] const Mock. Component = () => <div /> it('adds product count prop', () => { const Enhanced = with. Product. Count(Mock. Component) const wrapper = mount(<Enhanced products={products} />) const subject = wrapper. find(Mock. Component) expect(subject. prop('count')). to. Be(1) }) }) 84
Тестирование const with. Product. Count = with. Props( ({ products }) => ({ count: products. length }) ) describe('with. Product. Count decorator', () => { const products = [{ id: 1 }] const Mock. Component = () => <div /> it('adds product count prop', () => { const Enhanced = with. Product. Count(Mock. Component) const wrapper = mount(<Enhanced products={products} />) const subject = wrapper. find(Mock. Component) expect(subject. prop('count')). to. Be(1) }) }) 85
Тестирование const with. Product. Count = with. Props( ({ products }) => ({ count: products. length }) ) describe('with. Product. Count decorator', () => { const products = [{ id: 1 }] const Mock. Component = () => <div /> it('adds product count prop', () => { const Enhanced = with. Product. Count(Mock. Component) const wrapper = mount(<Enhanced products={products} />) const subject = wrapper. find(Mock. Component) expect(subject. prop('count')). to. Be(1) }) }) 86
Отладка const with. Greeting = compose( set. Display. Name('with. Greeting'), set. Prop. Types({ greeting: Prop. Types. string. required, children: Prop. Types. string. required }), only. Update. For. Prop. Types, map. Props(({ greeting, children }) => ({ text: `${greeting}, ${children}` })) ) const Label = ({ text }) => (<h 1>{text}</h 1>) const Greeting. Label = with. Greeting(Label) const App = () => <Greeting. Label greeting="Hi">John</Greeting. Label> 87
Отладка const with. Greeting = compose( set. Display. Name('with. Greeting'), set. Prop. Types({ greeting: Prop. Types. string. required, children: Prop. Types. string. required }), only. Update. For. Prop. Types, map. Props(({ greeting, children }) => ({ text: `${greeting}, ${children}` })) ) const Label = ({ text }) => (<h 1>{text}</h 1>) const Greeting. Label = with. Greeting(Label) const App = () => <Greeting. Label greeting="Hi">John</Greeting. Label> 88
Отладка const with. Greeting = compose( set. Display. Name('with. Greeting'), set. Prop. Types({ greeting: Prop. Types. string. required, children: Prop. Types. string. required }), only. Update. For. Prop. Types, map. Props(({ greeting, children }) => ({ text: `${greeting}, ${children}` })) ) const Label = ({ text }) => (<h 1>{text}</h 1>) const Greeting. Label = with. Greeting(Label) const App = () => <Greeting. Label greeting="Hi">John</Greeting. Label> 89
Отладка const with. Greeting = compose( set. Display. Name('with. Greeting'), set. Prop. Types({ greeting: Prop. Types. string. required, children: Prop. Types. string. required }), only. Update. For. Prop. Types, map. Props(({ greeting, children }) => ({ text: `${greeting}, ${children}` })) ) const Label = ({ text }) => (<h 1>{text}</h 1>) const Greeting. Label = with. Greeting(Label) const App = () => <Greeting. Label greeting="Hi">John</Greeting. Label> 90
Спасибо за внимание! dmitry. a. tsepelev@gmail. com @dmitrytsepelev Dmitry. Tsepelev 93