Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How to (maybe) structure a React application

keyserfaty
February 02, 2017

How to (maybe) structure a React application

Voy a contar un poco acerca de cómo separar la lógica de los datos de la de los componentes en aplicaciones de React me sirvió para crear aplicaciones que escalan mejor y que tienen muchos menos bugs. Esto significa, también, usar clases solo en muy pocos casos y usar funciones puras la mayor parte de las veces. Esto más la ayuda de Redux + Selectors + Sagas creo que puede ayudar a construir aplicaciones de React que ayuden a tener desarrollares más felices

keyserfaty

February 02, 2017
Tweet

More Decks by keyserfaty

Other Decks in Programming

Transcript

  1. Me llamo Karen Software Engineer @ Mad Mobile (Vanilla) Javascript

    Co-organizo Meetup.js Ciencias de la Educación & Ingeniería Electrónica @keyserfaty #
  2. 1. ¿Qué es la arquitectura de aplicaciones? 2. ¿Cuál sería

    la arquitectura ideal? 3. Tres problemas que tenés con React: . Primer problema: qué state usar para qué cosa . Segundo problema: cómo `queriar` el state . Tercer problema: cómo manejar la data async 4. Propuesta 5. Problemas de la propuesta 6. Conclusiones ¿De qué vamos a hablar? #
  3. #

  4. Firmitas Durabilidad. Tiene que se robusto y poder mantenerse en

    buenas condiciones a lo largo del tiempo #
  5. # import React from 'react'; import * as actions from

    './actions'; class TodoListContainer extends React.Component { initialState = { todos: [] } constructor (props) { super(props) this.state = this.initialState } componentWillMount () { // populate the state initially const { fetchAll } = this.props fetchAll() } onChange (id, text, status) { const { todos } = this.state const currentTodo = todos.filter(todo => todo.id === id) this.setState({ todos: [ ...todos, { ...currentTodo, text, status } ] }) } createTodo () { const { todos } = this.state const todo = { id: 1, text: '', status: 'available' } this.setState({ todos: todos.push(todo) }) } componentWillUnmount() { // clean state this.setState(this.initialState) } render () { const { todos } = this.props; return ( <div> <TodoList items={todos} /> </div> ) } } const mapStateToProps = state => ({ todos: state.todos }) const mapDispatchToProps = dispatch => ({ fetchAll: () => dispatch(actions.fetchAll()), saveTodo: () => dispatch(actions.saveTodo()), removeTodo: () => dispatch(actions.removeTodo()), }) export default ( mapStateToProps, mapDispatchToProps )(TodoListContainer)
  6. # import TodoList from './components/TodoList' import * as actions from

    './actions' const mapStateToProps = state => ({ todos: state.todos.list }) const mapDispatchToProps = dispatch => ({ fetchAll: () => dispatch(actions.fetchAll()), saveTodo: () => dispatch(actions.saveTodo()) deleteTodo: () => dispatch(actions.deleteTodo()) }) export default ( mapStateToProps, mapDispatchToProps )(withHooks(TodoList)) Container
  7. # import TodoList from './components/TodoList' import * as actions from

    './actions' const mapStateToProps = state => ({ todos: state.todos.list }) const mapDispatchToProps = dispatch => ({ fetchAll: () => dispatch(actions.fetchAll()), saveTodo: () => dispatch(actions.saveTodo()) deleteTodo: () => dispatch(actions.deleteTodo()) }) export default ( mapStateToProps, mapDispatchToProps )(TodoList) Container
  8. # ¿Cuándo state usar para qué cosa? React - Forms

    - Components autocontenidos Redux - Manejar la data (sync/async) - Cambios en la UI (excepto que sean parte de un componente autocontenido) ❓ ¿Otros? - Si no va a cambiar no pertenece al state => puede ser una constante
  9. # ¿Dónde poner mapStateToProps? Components/Container ¿Qué pasa cuando la data

    del state tiene que sufrir cambios para matchear con la UI?
  10. # const mapStateToProps = state => ({ status: selectors.getStatus(state), error:

    selectors.getError(state), endpoints: selectors.getEndpoints(state), searchValue: selectors.getSearchValue(state), isValidating: selectors.getValidationsViewStatus(state), exportCandidate: selectors.getExportCandidate(state), importCandidate: selectors.getImportCandidate(state), validationsErrors: selectors.getValidationErrorsWithRenamingOfStatus(state), //* Static content steps, popoverContent }); const mapDispatchToProps = dispatch => ({ onMount: () => { dispatch(endpointsActions.fetchEndpoints()); dispatch(connectionActions.fetchAll()); }, onUnmount: () => dispatch(endpointsActions.clearTestEndpoint()), handleValidate: () => dispatch(endpointsActions.validateEndpoint()), handleToggleValidationsView: () => dispatch(endpointsActions.toggleValidationsView()), handleOnChange: e => dispatch(endpointsActions.onChange({ name: e.target.name, value: e.target.value })), handleJsonOnChange: e => dispatch(endpointsActions.onJsonChange({ name: e.target.name, value: e.target.value })), handleSelectEndpoint: endpoint => dispatch(endpointsActions.selectExportEndpointCandidate({ endpoint })), handleUploadInit: name => dispatch(endpointsActions.uploadInit({ name })), handleUploadError: error => dispatch(endpointsActions.uploadError({ error })), handleUploadReady: file => dispatch(endpointsActions.uploadReady({ file })), handleUploadRemove: () => dispatch(endpointsActions.uploadRemove()) });
  11. # El único lugar por el que un component puede

    recibir data es por props desde el container
  12. # 2. ¿Qué pasa cuando la data del state tiene

    que sufrir cambios para matchear con la UI?
  13. # import * as selectors from './selectors' const mapStateToProps =

    state => ({ todos: selectors.getTodos(state) }) export const getTodos = state => state.todos.list
  14. # import * as selectors from './selectors' const mapStateToProps =

    state => ({ todos: selectors.getTodosWithAuthor(state) }) import { createSelector } from 'reselect' export const getTodos = state => state.todos.list export const getAuthors = state => state.author export const getTodosWithAuthor = createSelector( getTodos, getAuthor, (todos, author) => { const todoList = todos .reduce((res, todo) => { res.todo = todo res.author = author return res }, {}) return todoList } )
  15. #

  16. # import { createSelector } from ‘reselect' import { pipe

    } from 'ramda' export const getParsedList = createSelector( getParsedListBySearch, getActivePage, getSearchValue, getSorting, (items, activePage, searchValue, { column, asc }) => pipe( byColumn(column, asc), // sort by column && asc/desc byFuzzyName(searchValue)(items), // filter by search getVisibleItems(activePage, itemsPerPage), // filter items in the view )(items) )
  17. # ¿Qué pasa cuando la data tiene que sufrir cambios

    antes de poder mandarla en el request?
  18. # export function * saveThingWorker (action) { yield put(actions.saveThing.start()); const

    formData = yield select(selectors.getFormDataForTheThing); const { agent, error } = yield call(api.createAgent, formData); if (error) { return yield put(actions.saveThing.failure({ error })); } yield put(actions.saveThing.success({ thing })); } export function * saveThingWatcher () { yield takeEvery(actions.saveThing.type, saveThingWorker); }
  19. # Ventajas: 1- Mis funciones async solo hacen eso y

    no hacen handling de la data 2- Mis components están intactos
  20. # Actions Sagas State ¿Async? ¿Sync? Container Data Layer UI

    Layer Component Component Component Models Selectors Selectors