Skip to content

Instantly share code, notes, and snippets.

@goldoraf
Last active July 17, 2017 14:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save goldoraf/21e54049de3a67464a8c2d1ae6d13f0b to your computer and use it in GitHub Desktop.
Save goldoraf/21e54049de3a67464a8c2d1ae6d13f0b to your computer and use it in GitHub Desktop.

Aujourd'hui

export class Timeline extends Component {
  componentWillMount () {
    this.props.fetchTimeline()
  }

  render () {
    const { f, photos, fetchTimeline } = this.props
    return (
      <BoardView
        photos{photos.data}
        fetchStatus={photos.fetchStatus}
        hasMore={photos.hasMore}
        onFetchMore={() => fetchTimeline(photos.entries.length)}
      />
    )
  }
}

const mapStateToProps = (state, ownProps) => ({
  photos: getTimelinePhotos(state)
})

export const mapDispatchToProps = (dispatch, ownProps) => ({
  fetchTimeline: (skip) => dispatch(fetchTimeline(skip))
})

export default translate()(connect(
  mapStateToProps,
  mapDispatchToProps
)(Timeline))

Avec pour les action creators :

import { getCollection, fetchCollection } from 'redux-cozy-api'

export const getTimelinePhotos = (state) => getCollection('timeline')
export const fetchTimeline = (skip) => fetchCollection('timeline', 'io.cozy.files', {
  fields: ['dir_id', 'name', 'size', 'updated_at', 'metadata'],
  selector: {
    class: 'image',
    trashed: false
  },
  sort: {
    'metadata.datetime': 'desc'
  },
  skip
})

On pourrait améliorer un chouïa ainsi (le fetchMore est fourni par le selector de collections) :

export class Timeline extends Component {
  componentWillMount () {
    this.props.fetchTimeline()
  }

  render () {
    const { f, photos, fetchTimeline } = this.props
    return (
      <BoardView
        photos{photos.data}
        fetchStatus={photos.fetchStatus}
        hasMore={photos.hasMore}
        onFetchMore={photos.fetchMore}
      />
    )
  }
}

const mapStateToProps = (state, ownProps) => ({
  photos: getTimelinePhotos(state)
})

export const mapDispatchToProps = (dispatch, ownProps) => ({
  fetchTimeline: () => dispatch(fetchTimeline())
})

export default translate()(connect(
  mapStateToProps,
  mapDispatchToProps
)(Timeline))

Pros

  • on est habitué à cette structure là

Cons

  • fetch et "sélection" du state décorrélés
  • besoin d'une instance globale d'un "client" pour gérer plus facilement certaines choses (schemas, indexes...)

Inspiré d'Apollo

const Timeline = ({ photos }) => (
  <BoardView
    photos{photos.data}
    fetchStatus={photos.fetchStatus}
    hasMore={photos.hasMore}
    onFetchMore={photos.fetchMore}
  />
)

const TimelinePhotos = (ownProps) => ({
  photos: {
    type: 'io.cozy.files',
    selector: {
      class: 'image',
      trashed: false
    },
    sort: {
      'metadata.datetime': 'desc'
    }
  }
})

export default cozyConnect(TimelinePhotos)(Timeline)

Pros

  • parait nettement plus clean
  • facile d'avoir une instance client, qui en plus reste cachée

Cons

  • comment gérer les "mutations" ?
  • signifie un gros travail sur le HOC car on n'utiliserait plus le connect de react-redux et toutes ses optims

Solution client #1

export class Timeline extends Component {
 componentWillMount () {
   this.props.client.fetchCollection('timeline')
 }

 updatePhoto = (photo) => {
   this.props.client.updateDocument(photo)
 }

 render () {
   const { f, photos } = this.props
   return (
     <BoardView
       photos{photos.data}
       fetchStatus={photos.fetchStatus}
       hasMore={photos.hasMore}
       onFetchMore={photos.fetchMore}
       onEdit={this.updatePhoto}
     />
   )
 }
}

const mapStateToProps = (state, ownProps) => ({
 photos: getCollection(state, 'timeline')
})

export default cozyConnect(mapStateToProps)(Timeline)

cozyConnect vient injecter t, et surtout client qui détient une ref au store et peut ainsi dispatcher directement des actions. Il wrappe de surcroit le composant avec un connect classique.

Pros

  • plutôt clean
  • mutations faciles à gérer

Cons

  • le client qui "fuit" dans les composants, mais est-ce si grave ?
  • toujours une décorrélation entre le fetch et la sélection du state

Solution client #2

Utiliser un middleware redux pour fournir aux action creators génériques de redux-cozy-api une instance de client. Il faut pour cela donner une forme spécifique à ces actions creators :

export const fetchCollection = (collectionName) => ({
  type: FETCH_COLLECTION,
  promise: (client) => client.fetchCollection(collectionName)
})

Pros

  • le client n'apparait pas dans les composants

Cons

  • toujours une décorrélation entre le fetch et la sélection du state

Solution en cours d'implé

On utilise un middleware spécifique de type solution #2 et un HOC spécifique :

class Timeline extends Component {
  render () {
    const { photos, uploadPhoto } = this.props
    return (
      <BoardView
        photoLists={photoLists}
        fetchStatus={photos.fetchStatus}
        hasMore={photos.hasMore}
        photosContext='timeline'
        onFetchMore={photos.fetchMore}
        onUpload={uploadPhoto}
      />
    )
  }
}

export default cozyConnect('photos', () => fetchTimeline(), { uploadPhoto })(Timeline)

Avec :

import { fetchCollection, createFile } from '../../lib/redux-cozy-client'

export const fetchTimeline = (skip = 0) => fetchCollection(TIMELINE, FILES_DOCTYPE, {
  fields: ['dir_id', 'name', 'size', 'updated_at', 'metadata'],
  selector: {
    class: 'image',
    trashed: false
  },
  sort: {
    'metadata.datetime': 'desc'
  }
})

export const uploadPhoto = (photo) => createFile(photo, PHOTO_DIR)

Problème :

On ne peut pas toujours se reposer sur les 5-6 action creators de la lib, certaines actions demanderont toujours un workflow complexe avec le client.

import { fetchCollection, makeActionCreator } from '../../lib/redux-cozy-client'

export const fetchTimeline = (skip = 0) => fetchCollection(TIMELINE, FILES_DOCTYPE, {
  fields: ['dir_id', 'name', 'size', 'updated_at', 'metadata'],
  selector: {
    class: 'image',
    trashed: false
  },
  sort: {
    'metadata.datetime': 'desc'
  }
})

export const uploadPhoto = (photo, dirName) => makeActionCreator(async client => {
  const dir = await client.getOrCreateDirectory(dirName)
  return createFile(photo, PHOTO_DIR) // must return a generic action
})
@enguerran
Copy link

enguerran commented Jul 17, 2017

const fetcher = () => fetchTimeline()
const dispatchers = {
    uploadPhoto
}
const photosCozyConnect = cozyConnect('photos')
export default const photosCozyConnect(fetcher, dispatchers)(Timeline)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment