Skip to content

Commit

Permalink
Remove HOC & use custom hook in main practice (#48)
Browse files Browse the repository at this point in the history
  • Loading branch information
alefevre19 authored and AugustinLF committed Jul 3, 2019
1 parent e6980a2 commit 035f197
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 237 deletions.
16 changes: 1 addition & 15 deletions furtherWithReact.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,24 +53,10 @@ Avoid using refs for anything that can be done declaratively.



HIGHER-ORDER COMPONENTS
https://facebook.github.io/react/docs/higher-order-components.html
It allows to reuse component logic simply, in several components.
An HOC is a function that takes a component and returns a new component with additional props.
HOC is a compensation for the fact that we don't use inheritance in React.

An HOC doesn't modify the input component, nor does it use inheritance to copy its behavior.
Rather, an HOC composes the original component by wrapping it in a container component.
An HOC is a pure function with zero side-effects.



FUNCTION AS CHILD COMPONENT
https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9#.mayuaur98
You can pass to components a function as children. With it, we decouple the parent and child component
(only the composer knows how to apply parameters passed to the child component, the direct parent doesn't know)
With function as child component, there is no props name collision as there may be with decorators
(HOC pass named props, that could overwrite existing props).
(only the composer knows how to apply parameters passed to the child component, the direct parent doesn't know).


CONTEXT
Expand Down
6 changes: 0 additions & 6 deletions presentations/deck.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,6 @@ const Name = () => {
## Refs
A way to do imperative action on a component (call a function on it for instance), or access the DOM generated by a component.

---
## Higher Order Components
A function that takes a Component and returns a new one

It's just a wrapper around a component

---
## Function as a child component
children is nothing but a normal prop
Expand Down
54 changes: 0 additions & 54 deletions src/corrections/furtherWithReact/hoc.js

This file was deleted.

90 changes: 43 additions & 47 deletions src/corrections/mainPracticeCorrection/artistCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,59 +4,55 @@ import React from 'react';
import {getArtistInfoPromise} from '../../practice/main/libs/actionsHelpers';
import Card from './components/card';
import ThemeContext from './components/theme';
import DataFetcher from './components/dataFetcher';
import useDataFetcher from './hooks.js';
import {formatNumberToString, getSimilarArtistsNames} from './helpers';
import Tag from './tag';

const ArtistCard = ({artistName}) => (
<DataFetcher parameters={artistName} getData={getArtistInfoPromise}>
{({isLoading, data: artist}) => {
if (isLoading)
return (
<Card>
<div className="loading">Loading</div>
</Card>
);
const imgUrl = artist.image[2]['#text'];
const similarArtists = artist.similar && getSimilarArtistsNames(artist.similar);
const ArtistCard = ({artistName}) => {
const {isLoading, data: artist} = useDataFetcher(getArtistInfoPromise, artistName);
if (isLoading)
return (
<Card>
<div className="loading">Loading</div>
</Card>
);
const imgUrl = artist.image[2]['#text'];
const similarArtists = artist.similar && getSimilarArtistsNames(artist.similar);

return (
<ThemeContext.Provider value="pink">
<Card header={artistName}>
<div className="artist-card">
<div className="artist-card__main">
<a href={artist.url} target="_blank">
<img src={imgUrl} alt={artist.name} />
</a>
<div className="artist-card__details">
<div>
Play count:{' '}
{artist.stats &&
formatNumberToString(artist.stats.playcount)}
</div>
{artist.bio && (
<div
dangerouslySetInnerHTML={{
__html: artist.bio.summary,
}}
/>
)}
<div className="artist-card__tags">
{artist.tags &&
artist.tags.tag.map(tag => (
<Tag key={tag.name} name={tag.name} />
))}
</div>
</div>
return (
<ThemeContext.Provider value="pink">
<Card header={artistName}>
<div className="artist-card">
<div className="artist-card__main">
<a href={artist.url} target="_blank">
<img src={imgUrl} alt={artist.name} />
</a>
<div className="artist-card__details">
<div>
Play count:{' '}
{artist.stats && formatNumberToString(artist.stats.playcount)}
</div>
{artist.bio && (
<div
dangerouslySetInnerHTML={{
__html: artist.bio.summary,
}}
/>
)}
<div className="artist-card__tags">
{artist.tags &&
artist.tags.tag.map(tag => (
<Tag key={tag.name} name={tag.name} />
))}
</div>
<div>Similar Artists: {similarArtists}</div>
</div>
</Card>
</ThemeContext.Provider>
);
}}
</DataFetcher>
);
</div>
<div>Similar Artists: {similarArtists}</div>
</div>
</Card>
</ThemeContext.Provider>
);
};
ArtistCard.propTypes = {
artistName: PropTypes.string.isRequired,
};
Expand Down
111 changes: 53 additions & 58 deletions src/corrections/mainPracticeCorrection/artistsList.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ import Card from './components/card';
import ThemeContext from './components/theme';
import ArtistRow from './artistRow';
import SearchArtist from './searchArtist';
import DataFetcher from './components/dataFetcher';
import useDataFetcher from './hooks.js';

const ArtistsList = ({openArtistCard}) => {
const [favorites, setFavorites] = React.useState([]);
const [onlyFavorites, setOnlyFavorites] = React.useState(false);
const [search, setSearch] = React.useState('');
const [sorted, sort] = React.useState(false);
const {isLoading, data: artistsList} = useDataFetcher(getTopArtistsPromise);

const sortByPlayCount = () => {
sort(prevSort => !prevSort);
Expand All @@ -29,65 +30,59 @@ const ArtistsList = ({openArtistCard}) => {
);
};

return (
<DataFetcher getData={getTopArtistsPromise}>
{({isLoading, data: artistsList}) => {
const artists = getArtistsList(
artistsList || [],
sorted,
search,
onlyFavorites ? favorites : null
);
const artists = getArtistsList(
artistsList || [],
sorted,
search,
onlyFavorites ? favorites : null
);

const header = (
<div>
Top Artists
<div style={{display: 'flex'}}>
<SearchArtist search={search} searchArtist={setSearch} />
<button style={{marginLeft: '5px'}} onClick={filterFavorites}>
{onlyFavorites ? 'Display all' : 'Display only favorites'}
</button>
<button style={{marginLeft: '5px'}} onClick={sortByPlayCount}>
{sorted ? 'Remove sorting' : 'Sort by play count'}
</button>
</div>
</div>
);
const header = (
<div>
Top Artists
<div style={{display: 'flex'}}>
<SearchArtist search={search} searchArtist={setSearch} />
<button style={{marginLeft: '5px'}} onClick={filterFavorites}>
{onlyFavorites ? 'Display all' : 'Display only favorites'}
</button>
<button style={{marginLeft: '5px'}} onClick={sortByPlayCount}>
{sorted ? 'Remove sorting' : 'Sort by play count'}
</button>
</div>
</div>
);

return (
<ThemeContext.Provider value="green">
<Card header={header}>
{isLoading ? (
<div className="loading">Loading</div>
) : (
<table>
<thead>
<tr>
<th />
<th>Name</th>
<th>Play count</th>
<th>Link</th>
<th>Favorites ({favorites.length})</th>
</tr>
</thead>
<tbody>
{artists.map(artist => (
<ArtistRow
key={artist.name}
artist={artist}
changeFavorite={changeFavorite}
openArtistCard={openArtistCard}
favorite={favorites.includes(artist.name)}
/>
))}
</tbody>
</table>
)}
</Card>
</ThemeContext.Provider>
);
}}
</DataFetcher>
return (
<ThemeContext.Provider value="green">
<Card header={header}>
{isLoading ? (
<div className="loading">Loading</div>
) : (
<table>
<thead>
<tr>
<th />
<th>Name</th>
<th>Play count</th>
<th>Link</th>
<th>Favorites ({favorites.length})</th>
</tr>
</thead>
<tbody>
{artists.map(artist => (
<ArtistRow
key={artist.name}
artist={artist}
changeFavorite={changeFavorite}
openArtistCard={openArtistCard}
favorite={favorites.includes(artist.name)}
/>
))}
</tbody>
</table>
)}
</Card>
</ThemeContext.Provider>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as React from 'react';
import PropTypes from 'prop-types';

const DataFetcher = ({children, getData, parameters}) => {
const useDataFetcher = (getData, parameters) => {
const [state, dispatch] = React.useReducer(
(state, action) => {
switch (action.type) {
Expand Down Expand Up @@ -43,13 +42,7 @@ const DataFetcher = ({children, getData, parameters}) => {
[parameters]
);

return children({data: state.data, isLoading: state.isLoading});
return {data: state.data, isLoading: state.isLoading};
};

DataFetcher.propTypes = {
children: PropTypes.func.isRequired,
getData: PropTypes.func.isRequired,
parameters: PropTypes.any,
};

export default DataFetcher;
export default useDataFetcher;
2 changes: 0 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,13 @@ import './examples/intro/intro';
/* Further with React */
// import './examples/secondCourse/events';
// import './examples/secondCourse/refs';
// import './examples/secondCourse/hoc';
// import './examples/secondCourse/functionAsChild';
// import './examples/secondCourse/context';

/* Further with React Practices */
// import './practice/furtherWithReact/events';
// import './practice/furtherWithReact/forms';
// import './practice/furtherWithReact/refs';
// import './practice/furtherWithReact/hoc';
// import './practice/furtherWithReact/functionAsChild';
// import './practice/furtherWithReact/context';

Expand Down
1 change: 0 additions & 1 deletion src/practice/furtherWithReact/functionAsChild.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import ReactDOM from 'react-dom';
/* TODO
* - on désire avoir une gestion générique des formulaires :
* créer un render prop qui gère le changement de state et le test des données au submit
* - Vous pouvez repartir du HOC créé au TP précédent
*/

const validateForm = ({age, firstname}) => (firstname && age > 0 ? false : true);
Expand Down
Loading

0 comments on commit 035f197

Please sign in to comment.