Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Avoid boilerplate and repetitive code in React ...

Avatar for thib92 thib92
November 28, 2018

Avoid boilerplate and repetitive code in React Native using Galette - React Native London @ Birdie

Do you feel like you're always writing the same code over and over again?
Loading lists from an API, pagination, infinite scroll, handling loading and error states, checking for non-null values, etc.
Galette is a library that allows you to avoid repeating yourself while handling all of these patterns so that you can focus on your useful domain logic.
I will introduce you to the library with concrete examples.

Avatar for thib92

thib92

November 28, 2018
Tweet

More Decks by thib92

Other Decks in Programming

Transcript

  1. Avoid boilerplate and repetitive code in React Native using Galette

    React Native London - November 2018 @ Birdie 1
  2. About me ! • French engineering student • Intern Full

    Stack Software engineer @ Birdie • Passionate about new tech about web, mobile and more! React Native London - November 2018 @ Birdie 2
  3. 1. Handling lists a. Loading a list from API export

    default function clientsReducer(state = [], action) { switch (action.type) { case GET_CLIENTS: return action.clients; default: return state; } } React Native London - November 2018 @ Birdie 7
  4. 1. Handling lists b. Displaying a list const MyComponent =

    (props) => ( <FlatList data={props.clients} renderItem={({item}) => <MyClient client={item} />} /> ) export default connect( state => ({ clients: state.clients }) )(MyComponent); React Native London - November 2018 @ Birdie 8
  5. 1. Handling lists b. Handling loading and error states export

    default function clientsReducer(state = {}, action) { switch (action.type) { case GET_CLIENTS: return { clients: state.clients, loading: true, error: false, }; case GET_CLIENTS_SUCCESS: return { clients: action.clients, loading: false, error: false, }; case GET_CLIENTS_ERROR: return { clients: state.clients, loading: false, error: true, } default: return state; } } React Native London - November 2018 @ Birdie 9
  6. 1. Handling lists c. Displaying loading and error states const

    MyComponent = (props) => { if (props.clients.loading) { return <Loading />; } if (props.clients.error) { return <Error />; } return ( <FlatList data={props.clients.clients} renderItem={({item}) => <MyClient client={item} />} /> ) } export default connect( state => ({ clients: state.clients }) )(MyComponent); React Native London - November 2018 @ Birdie 10
  7. 1. Handling lists d. Pagination 1. Make your action accept

    a page parameter 2. Make your async middleware use this parameter (Thunk, Saga, etc.) 3. Change the reducer case GET_CLIENTS_SUCCESS: return { // This is also prone to bugs clients: [...state.clients, ...action.clients], loading: false, error: false, page: state.page + 1 }; React Native London - November 2018 @ Birdie 11
  8. 1. Handling lists c. Infinite scroll const MyComponent = (props)

    => { if (props.clients.loading) { return <View>Loading</View>; } if (props.clients.error) { return <View>Error</View>; } return ( <FlatList data={props.clients.clients} renderItem={({item}) => <MyClient client={item} />} onEndReached={() => this.props.loadMoreClients()} onEndReachedThreshold={0.8} onRefresh={() => this.props.refreshClients()} /> ) } export default connect( state => ({ clients: state.clients }), dispatch => bindActionCreators({ loadMoreClients, refreshClients }) )(MyComponent); React Native London - November 2018 @ Birdie 12
  9. The solution... ...is called Galette • Open-source package with helpers

    for common patterns in React and Redux • Helps you reduce your boilerplate and errors • Gain a lot of time React Native London - November 2018 @ Birdie 15
  10. 1. Handling lists The reducer export default clientsReducer(state = initialState,

    action) { return reduceListAndItems(state, action, { // The prefix for each of the actions to handle actionPrefix: 'GET_CLIENTS', // In your `state`, the key to be used for the list. listKeyInState: 'clients', // A function responsible of extracting the identifier for each item itemIdentifierResolver: client => client.id, // How to get the payload from a "success" action payloadResolver: action => action.clients }); } React Native London - November 2018 @ Birdie 16
  11. 1. Handling lists The component <ScrollableCollection collection={collection} renderRow={({item}) => <MyClient

    client={item} /> onRefresh={this.getCareLog} zeroStatePlaceHolderMessage="We don't have anything to display here at the moment" /> React Native London - November 2018 @ Birdie 17
  12. 2. Displaying an item efficiently • Storing an array in

    the reducer and using • users.find(user => user.id === id) • Performance issues with large lists on every render • Displaying an item from an API, ensuring the value is fetched • Repetitive null-checks • Boilerplate to fetch the entity React Native London - November 2018 @ Birdie 19
  13. 2. Displaying an item efficiently // Selector export const getUserByUsername

    = username => state.users.find(user => user.username === username) // Component class UserScreen { constructor(props) { super(props); if (!props.user) { props.fetchUser(); } } render() { if (!this.props.user) { return null; } console.log('Render user from: ', this.props.user); } } React Native London - November 2018 @ Birdie 20
  14. 2. Displaying an item efficiently class UserScreen extends Component {

    render() { console.log('Render user from: ', this.props.user); } } export default connectEntity(UserScreen, { // The prop to inject into property: 'user', // The action to dispatch if the item is not loaded yet loadEntityAction: loadUser, // Compatible with Collection entitySelector: (state, username) => state.users[username], // Get the user id from the navigation props identifierFromPropsResolver: props => props.navigation.state.params.username }); React Native London - November 2018 @ Birdie 21
  15. 3. Redux boilerplate • Boilerplate in reducers • Boilerplate in

    actions • Handling errors React Native London - November 2018 @ Birdie 23
  16. 3. Redux boilerplate a. Boilerplate in reducers export default function

    reducer(state = initialState, action) { switch(action.type) { case GET_USER: // ... break; case GET_USER_SUCCESS: // ... break; case GET_USER_FAILURE: // ... break; default: return state; } } React Native London - November 2018 @ Birdie 24
  17. 3. Redux boilerplate a. Boilerplate in reducers export default createMappedReducer(initialState,

    { [GET_USER]: (state, action) => { // ... }, [GET_USER_SUCCESS]: (state, action) => { // ... }, [GET_USER_FAILURE]: (state, action) => { // ... }, }); React Native London - November 2018 @ Birdie 25
  18. 3. Redux boilerplate b. Boilerplate in actions // Action export

    const types = { LOAD_USER: 'LOAD_USER', }; export const loadUser = (username) => ({ type: types.LOAD_USER, username }); // Reducer or middleware action.type === types.LOAD_USER React Native London - November 2018 @ Birdie 26
  19. 3. Redux boilerplate b. Boilerplate in actions // Action export

    const loadUser = typedActionCreatorFactory( 'LOAD_USER', username => ({ username }) ); // Reducer or middleware action.type === loadUser.type React Native London - November 2018 @ Birdie 27
  20. 3. Redux boilerplate c. Handling errors const App => ()

    => ( <Provider store={store}> <ErrorWrapper> {/** your router/components **/} </ErrorWrapper> </Provider> ) React Native London - November 2018 @ Birdie 28
  21. 3. Redux boilerplate c. Handling errors // In the component...

    this.props.reportError(new Error('Something went wrong.')); React Native London - November 2018 @ Birdie 29
  22. 3. Redux boilerplate c. Handling errors // In a saga...

    const mySaga = handleSagaErrors(function*() { // Your own saga code... // yield ...; }); React Native London - November 2018 @ Birdie 30
  23. What's next? • A lot more things already done •

    More typings, more docs • Our next problems, solved • Your next problems, solved? React Native London - November 2018 @ Birdie 32
  24. Thank you ❤ Tweet at us @BirdieCare (Btw we are

    hiring) React Native London - November 2018 @ Birdie 34