Introducción a C++

Gerson Garrido Manislla

Universidad Nacional de Ingeniería

Escuela de Ciencia de la Computación

Acerca de mí

  • Senior UI Developer at PureCars, a digital marketing and automation company in the automotive space

  • Moonlights as a consultant with his brother where they work with small businesses to create modern dashboard applications.

  • Love ultimate frisbee and watching NFL

Joel Kanzelmeyer

C++

C++

  • Creado por Bjarne S. en 1983
  • Multiparadigma, Orientado a objetos*
  • Es un lenguaje potente

Empezemos!

/* Programa que nos muestra en consola Hola mundo!*/
// Esto también es un comentario 

#include <iostream>

int main()
{
    std::cout << "Hello World!";
    return 0;
}

Vamos a explicar linea a linea

/* Programa que nos muestra en consola Hola mundo!*/
// Esto también es un comentario 

Estos son comentarios es decir que no afectan el comportamiento del programa. Los programadores usan los comentarios para documentar el código.

#include <iostream>

Esta linea es una directivas leídas e interpretadas el preprocesador. Son líneas especiales interpretadas antes de que empiece la compilación del programa mismo. En este caso, la directiva #include <iostream>, permite realizar operaciones de entrada y salida estándar, como escribir la salida de este programa.

int main ()

La función llamada main() es una función especial en todos los programas C ++. Es la función llamada cuando se ejecuta el programa. La ejecución de todos los programas de C ++ comienza con la función principal.

continuación..

{ 
    ... 
}

std::cout << "Hello World!";

La llave abierta "{" indica el comienzo de la definición de la función principal, y la llave de cierre "}" , indica su final. Todas las funciones utilizan llaves para indicar el comienzo y el final de sus definiciones.

std::cout << "Hello World!";

Esta línea es una declaración de C ++. Una declaración es una expresión que realmente puede producir algún efecto.

Esta declaración tiene tres partes: Primero, std :: cout, que signfica standard character output (salida de caracteres estándar). En segundo lugar, el operador de inserción (<<), que indica que lo que sigue se inserta en std :: cout. Finalmente, una oración entre comillas ("¡Hola mundo!"), Es el contenido insertado en la salida estándar.

Observe que la sentencia termina con un punto y coma (;). Este carácter marca el final de la declaración. Todas las instrucciones de C ++ deben finalizar con un carácter punto y coma.

Fácil de entender

Ahora vamos a ejecutarlo

Easy to reason about

(RIP Gene Wilder)

Good separation of concerns

Your business logic is kept out of your views

Developer adoption

A community of great developers support Redux

Middleware

Enhance base functionality with custom middleware

React Boilerplate

The struggle is real

JS Tool Fatigue

React Boilerplate to the rescue!

  • Most stable/production-ready solution I've found
  • Follows good practices for large React apps
  • Server-side rendering
  • Code splitting (lazy load code when needed)
  • Hot module reloading (excellent for debugging)
  • ES6 support and CSS modules/PostCSS

React Boilerplate to the rescue!

Best Practices

Redux at Scale

Best Practice #1

Smart containers and dumb components

Redux at Scale

Dumb Components

(Presentational)

Dumb Component Example

const DumbComponent = (props) => {

  const { text, onChange } = props;
  
  return (
    <div>
      <p>Your text: {text}</p>
      <input value={text} onChange={onChange} />
    </div>
  );

};

Smart Containers

(Stateful)

Smart Container Example

//...
import { updateText } from '../actions';
//...

class SmartContainer extends React.Component {
  render() {
    const { text, updateText } = this.props;
    return (
      <DumbComponent text={text} onChange={updateText} />
    );
  }
}

const mapStateToProps = (state) => ({
  text: state.text
});

const mapDispatchToProps = (dispatch) => ({
  updateText: (evt) => dispatch(updateText(evt.target.text))
});

export default connect(mapStateToProps, mapDispatchToProps)(SmartContainer);

Benefits

  • Better separation of concerns
  • Better reusability
  • A library of UI components

Best Practice #2

Immutability

Redux at Scale

Equality checking in Javascript

1 === 1
'string' === 'string'
true === true
const obj1 = { prop: ’someValue’ };
const obj2 = { prop: ’someValue’ };
console.log(obj1 === obj2);   // false

Why is this a problem?

Fine-tuning React Performance

shouldComponentUpdate

class NewsFeed extends React.Component {

  shouldComponentUpdate(nextProps) {
    return !_.isEqual(this.props.articles, nextProps.articles) ||
      !_.isEqual(this.props.user, nextProps.user);
  }

  render() {
    const { user, articles } = this.props;
    return (
      <div>
        <p>Hello, {user.firstName}! Here is your news for the day:</p>
        <div>
        {
          articles.map((article) => (
            <Article article=[article} />
          ))
        }
        </div>
      </div>
    );
  }

}

Immutable.js

shouldComponentUpdate with Immutable.js


  shouldComponentUpdate(nextProps) {
    return this.props.articles !== nextProps.articles ||
      this.props.user !== nextProps.user;
  }

  shouldComponentUpdate(nextProps, nextState) {
    return shallowCompare(this, nextProps, nextState);
  }

react-addons-shallow-compare

Benefits

  • Improves performance
  • Simplifies shouldComponentUpdate
  • Easier to reason about

Best Practice #3

Normalized state tree

Redux at Scale

API returns deeply nested objects

// API response
[
  {
    "id": 1,
    "title": "Why React is Awesome",
    "author": {
      "id": 1,
      "name": "Joel Kanzelmeyer"
    }
  },
  {
    "id": 2,
    "title": "Why Redux is Great",
    "author": {
      "id": 1,
      "name": "Joel Kanzelmeyer"
    }
  }
]
// Redux state
{
  articles: [{
    id: 1,
    title: 'Why React is Awesome',
    author: {
      id: 1,
      name: 'Joel Kanzelmeyer'
    }
  }, {
    id: 2,
    title: 'Why Redux is Great',
    author: {
      id: 1,
      name: 'Joel Kanzelmeyer'
    }
  }]
}
// Redux state
{
  articles: [{
    id: 1,
    title: 'Why React is Awesome',
    author: {
      id: 1,
      name: 'Joel Kanzelmeyer'
    }
  }, {
    id: 2,
    title: 'Why Redux is Great',
    author: {
      id: 1,
      name: 'Joel Cancelmeyer'
    }
  }]
}

normalizr

Turns this...

// API response
[
  {
    "id": 1,
    "title": "Why React is Awesome",
    "author": {
      "id": 1,
      "name": "Joel Kanzelmeyer"
    }
  },
  {
    "id": 2,
    "title": "Why Redux is Great",
    "author": {
      "id": 1,
      "name": "Joel Kanzelmeyer"
    }
  }
]
// Normalized result
{
  entities: {
    authors: {
      1: {
        id: 1,
        name: 'Joel Kanzelmeyer'
      }
    },
    articles: {
      1: {
        id: 1,
        name: 'Why React is Awesome'
      },
      2: {
        id: 2,
        name: 'Why Redux is Great'
      }
    }
  },
  result: {
    authors: [1],
    articles: [1, 2]
  }
}

into this.

Example with normalizr

// Redux state
{
  articles: [{
    id: 1,
    title: 'Why React is Awesome',
    author: {
      id: 1,
      name: 'Joel Kanzelmeyer'
    }
  }, {
    id: 2,
    title: 'Why Redux is Great',
    author: {
      id: 1,
      name: 'Joel Kanzelmeyer'
    }
  }]
}
// Redux state with normalizr
{
  authors: {
    byId: {
      1: {
        id: 1,
        name: 'Joel Kanzelmeyer'
      }
    },
    all: [1]
  },
  articles: {
    byId: {
      1: {
        id: 1,
        title: 'Why React is Awesome',
        author: 1
      },
      2: {
        id: 2,
        title: 'Why Redux is Great',
        author: 1
      }
    },
    all: [1, 2]
  }
}

redux-normalizr-middleware

Action/reducer example

import { Schema, arrayOf } from 'normalizr';  

// define your schemas
const article = new Schema('articles');
const author = new Schema('authors');

// define nesting rules
article.define({
  author: author
});

export const getArticlesSuccess = (response) => ({
  type: 'GET_ARTICLES_SUCCESS',
  payload: response.body,
  meta: {
    // provide middleware with normalizr schema
    schema: arrayOf(article)
  }
});
// API response
[
  {
    "id": 1,
    "title": "Why React is Awesome",
    "author": {
      "id": 1,
      "name": "Joel Kanzelmeyer"
    }
  },
  {
    "id": 2,
    "title": "Why Redux is Great",
    "author": {
      "id": 1,
      "name": "Joel Kanzelmeyer"
    }
  }
]
// define initial state for reducers
const initialState = Immutable.Map({
  byId: Immutable.Map(),
  all: Immutable.Set()
});

// define default reducer that will merge in normalized state
const defaultReducer = (schemaName, state, action) => {
  if (action.entities && action.entities[schemaName]) {
    return state
      .mergeIn(['byId'], action.entities[schemaName])
      .mergeIn(['all'], action.result[schemaName]);
  }
  return state;
}

// authors reducer
const authors = (state = initialState, action) => {
  switch (action.type) {
    case 'UPDATE_AUTHOR':
      return state.mergeIn(['byId', action.id], action.author);
    default:
      return defaultReducer('authors', state, action);
  }
};

// articles reducer
const articles = (state = initialState, action) => {
  return defaultReducer('articles', state, action);
};

// root reducer
const reducer = combineReducers({
  authors,
  articles
});

Benefits

  • Normalizes deeply nested objects
  • Prevents duplicate state
  • Normalization can happen in middleware

Problems You'll Encounter

Redux at Scale

(and solutions to them)

Problem #1

Asynchronous/chained actions

(actions with side-effects)

Redux at Scale

Example

// API service
const getBooks = () =>
  fetch('/api/books')
    .then(handleErrors)
    .then(parseJSON);
// View
vm.loadBooks = () => {
  vm.booksLoading = true;
  booksApi.getBooks()
    .then((books) => {
      vm.booksLoading = false;
      vm.books = books;
    })
    .catch((err) => {
      vm.booksLoading = false;
      vm.error = err;
    });
};

MVC

// Actions
const getBooks = () => {
  return fetch('/api/books')
    .then(handleErrors)
    .then(parseJSON)
    .then((books) => ({
      type: 'GET_BOOKS',
      payload: books
    });
};
onComponentWillMount() {
  this.props.getBooks();
  // Uncaught Error: Actions must be plain objects.
  // Use custom middleware for async actions.
}

React/Redux

What We Really Need

// Actions

const getBooks = () => ({
  type: 'GET_BOOKS'
});

const getBooksSuccess = (books) => ({
  type: 'GET_BOOKS_SUCCESS',
  payload: books
});

const getBooksFailure = (error) => ({
  type: 'GET_BOOKS_FAILURE',
  payload: error
});
// Reducer

const booksReducer = (state, action) => {
  switch (action.type) {
    case 'GET_BOOKS':
      return {
        ...state,
        loading: true,
        error: null
      };
    case 'GET_BOOKS_SUCCESS':
      return {
        ...state,
        loading: false,
        books: action.payload
      };
    case 'GET_BOOKS_FAILURE':
      return {
        ...state,
        loading: false,
        error: action.payload
      };
    default:
      return state;
  }
};

redux-thunk

Solution #1

Thunked Action

function incrementIfOdd() {
  return (dispatch, getState) => {
    const { counter } = getState();

    if (counter % 2 === 0) {
      return;
    }

    dispatch(increment());
  };
}

Example with redux-thunk

// Actions

// create a thunked action
const getBooks = () => {
  return (dispatch, getState) => {
    // check to see if books have already been fetched
    if (!getState().books) {
      // if not, dispatch the GET_BOOKS action
      dispatch({
        type: 'GET_BOOKS'
      });
      // then you make your http request
      booksApi.getBooks()
        .then((books) => {
          // dispatch success action
          dispatch(getBooksSuccess(books));
        })
        .catch((error) => {
          // dispatch failure action
          dispatch(getBooksFailure(error));
        });
    }
  };
};

const getBooksSuccess = (books) => ({
  type: 'GET_BOOKS_SUCCESS',
  payload: books
});

const getBooksFailure = (error) => ({
  type: 'GET_BOOKS_FAILURE',
  payload: error
});
// in your component

onComponentWillMount() {
  this.props.getBooks();
  /**
    dispatches GET_BOOKS and
    then when response comes back
    it will dispatch either
    GET_BOOKS_SUCCESS or
    GET_BOOKS_FAILURE
  **/
}

render() {
  const {
    isLoading,
    error,
    books
  } = this.props;
  if (isLoading) {
    return (
      <div>loading...</div>
    );
  }
  if (error) {
    return (
      <div>error: {error}</div>
    );
  }
  return (
    <BooksList books={books} />
  );
}

redux-thunk

  • Handle side-effects within your action creators
  • Able to unit test, must mock redux store and http requests

redux-saga

Solution #2

(My preference, for what it's worth)

redux-saga

class UserComponent extends React.Component {
  ...
  onSomeButtonClicked() {
    const { userId, dispatch } = this.props
    dispatch({type: 'USER_FETCH_REQUESTED', payload: {userId}})
  }
  ...
}
import { takeEvery } from 'redux-saga'
import { call, put } from 'redux-saga/effects'
import Api from '...'

// worker Saga: will be fired on USER_FETCH_REQUESTED actions
function* fetchUser(action) {
   try {
      const user = yield call(Api.fetchUser, action.payload.userId);
      yield put({type: 'USER_FETCH_SUCCEEDED', user: user});
   } catch (e) {
      yield put({type: 'USER_FETCH_FAILED', message: e.message});
   }
}

/*
  Starts fetchUser on each dispatched 'USER_FETCH_REQUESTED' action.
  Allows concurrent fetches of user.
*/
function* mySaga() {
  yield* takeEvery('USER_FETCH_REQUESTED', fetchUser);
}

export default mySaga;

Why I prefer redux-saga over redux-thunk

Easier to reason about

import { takeEvery } from 'redux-saga'
import { call, put, select } from 'redux-saga/effects'
import { getBooksSuccess, getBooksFailure } from './actions';

const selectBooks = (state) => state.books;

function* fetchBooks(action) {
  // check to see if books exist in state already
  const books = yield select(selectBooks);
  if (!books) {
    try {
      // call booksApi.getBooks to get books from API
      const _books = yield call(booksApi.getBooks);
      // dispatch GET_BOOKS_SUCCESS
      yield put(getBooksSuccess(_books));
    } catch (e) {
      // if request fails, dispatch GET_BOOKS_FAILURE
      yield put(getBooksFailure(e.message));
    }
  }
}

// "listener" function that will listen for GET_BOOKS actions
function* booksSaga() {
  yield* takeEvery('GET_BOOKS', fetchBooks);
}

export default booksSaga;
// Actions

// create a thunked action
const getBooks = () => ({
  type: 'GET_BOOKS'
});

const getBooksSuccess = (books) => ({
  type: 'GET_BOOKS_SUCCESS',
  payload: books
});

const getBooksFailure = (error) => ({
  type: 'GET_BOOKS_FAILURE',
  payload: error
});

Why I prefer redux-saga over redux-thunk

Testing is simpler

import test from 'tape';

import { put, call } from 'redux-saga/effects'
import { delay } from 'redux-saga'
import { incrementAsync } from './sagas'

test('incrementAsync Saga test', (assert) => {
  const gen = incrementAsync()

  assert.deepEqual(
    gen.next().value,
    call(delay, 1000),
    'incrementAsync Saga must call delay(1000)'
  )

  assert.deepEqual(
    gen.next().value,
    put({type: 'INCREMENT'}),
    'incrementAsync Saga must dispatch an INCREMENT action'
  )

  assert.deepEqual(
    gen.next(),
    { done: true, value: undefined },
    'incrementAsync Saga must be done'
  )

  assert.end()
});
import { put, call } from 'redux-saga/effects'
import { delay } from 'redux-saga'

export function* incrementAsync() {
  // use the call Effect
  yield call(delay, 1000)
  yield put({ type: 'INCREMENT' })
}

Why I prefer redux-saga over redux-thunk

Highly de-coupled

  • Your actions no longer contain business logic
  • Business logic can be encapsulated in sagas
  • Add new logic to existing actions by creating sagas

redux-saga

  • Keeps your actions pure
  • Handle side-effects in a "separate thread" with sagas
  • Able to unit test without mocking surrounding environment

Problem #2

Selecting state, especially derived state, is painful and non-performant

Redux at Scale

Example

// SomeComponent.js
...
const mapStateToProps = (state) => {
  const books = state.books; // Immutable map
  const filterText = state.filterText;
  return {
    filteredBooks: books.filter((book) => {
      return book.name.includes(filterText);
    })
  };
};
...
export default connect(mapStateToProps)(SomeComponent);

Why is this a problem?

const mapStateToProps = (state) => {
  const books = state.books; // Immutable map
  const filterText = state.filterText;
  return {
    filteredBooks: books.filter((book) => {
      return book.name.includes(filterText);
    })
  };
};
  • mapStateToProps is called on every state change
  • Shallowly compares and re-renders only when changed
  • Filtering, sorting, mapping, etc always returns new instance

reselect

Solution

reselect

// selectors.js
import { createSelector } from 'reselect';

const booksSelector = (state) => state.books;
const filterTextSelector = (state) => state.filterText;

export const filteredBooksSelector = createSelector(
  booksSelector,
  filterTextSelector,
  (books, filterText) =>
    books.filter((book) => {
      return book.name.includes(filterText);
    })
);
// SomeComponent.js
import { filteredBooksSelector } from './selectors.js';
...
const mapStateToProps = (state) => {
  return {
    filteredBooks: filteredBooksSelector(state)
  };
};
...
export default connect(mapStateToProps)(SomeComponent);

reselect

  • Better performance via memoized selectors
  • Selectors are now composable
  • Added benefit of encapsulating state selection logic

Problem #3

Too much boilerplate code and too reliant on string constants

Redux at Scale

export const ADD_TODO = 'ADD_TODO';
export const REMOVE_TODO = 'REMOVE_TODO';
export const EDIT_TODO = 'EDIT_TODO';
export const COMPLETE_TODO = 'COMPLETE_TODO';
export const COMPLETE_ALL = 'COMPLETE_ALL';
export const CLEAR_COMPLETED = 'CLEAR_COMPLETED';
import * as types from './constants';

const addTodo = (text) => ({
  type: types.ADD_TODO,
  payload: { text }
});

const removeTodo = (id) => ({
  type: types.REMOVE_TODO,
  payload: { id }
});

const editTodo = (id, text) => ({
  type: types.EDIT_TODO,
  payload: { id, text }
});

const completeTodo = (id) => ({
  type: types.COMPLETE_TODO,
  payload: { id }
});

const completeAll = () => ({
  type: types.COMPLETE_ALL
});

const clearCompleted = () => ({
  type: types.CLEAR_COMPLETED
});
import {
  ADD_TODO, DELETE_TODO, EDIT_TODO, COMPLETE_TODO,
  COMPLETE_ALL, CLEAR_COMPLETED
} from './constants';

const initialState = [
  {
    text: 'Use Redux',
    completed: false,
    id: 0
  }
]

export default function todos(state = initialState, action) {
  switch (action.type) {
    case ADD_TODO:
      return [
        {
          id: state.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1,
          completed: false,
          text: action.text
        },
        ...state
      ]

    case DELETE_TODO:
      return state.filter(todo =>
        todo.id !== action.id
      )

    case EDIT_TODO:
      return state.map(todo =>
        todo.id === action.id ?
          { ...todo, text: action.text } :
          todo
      )

    case COMPLETE_TODO:
      return state.map(todo =>
        todo.id === action.id ?
          { ...todo, completed: !todo.completed } :
          todo
      )

    case COMPLETE_ALL:
      const areAllMarked = state.every(todo => todo.completed)
      return state.map(todo => ({
        ...todo,
        completed: !areAllMarked
      }))

    case CLEAR_COMPLETED:
      return state.filter(todo => todo.completed === false)

    default:
      return state
  }
}

Constants

Actions

Reducer

Example

redux-act

Solution

createAction

Simpler, more consistent way to create redux action creators

const addTodo = createAction('Add todo');

addTodo('content');
// return {
//   type: '[1] Add todo',
//   payload: 'content'
// }

const editTodo = createAction(
  'Edit todo',
  (id, content) => ({id, content})
);

editTodo(42, 'the answer');
// return {
//   type: '[2] Edit todo',
//   payload: {
//     id: 42,
//     content: 'the answer'
//   }
// }

const serializeTodo = createAction('SERIALIZE_TODO');
serializeTodo(1);
// return { type: 'SERIALIZE_TODO', payload: 1 }

createReducer

Simpler, more consistent way to create redux reducers

const increment = createAction();
const add = createAction();


// First pattern
const reducerMap = createReducer({
  [increment]: (state) => state + 1,
  [add]: (state, payload) => state + payload
}, 0);


// Second pattern
const reducerFactory = createReducer(function (on, off) {
  on(increment, (state) => state + 1);
  on(add, (state, payload) => state + payload);
  // 'off' remove support for a specific action
}, 0);
import { createAction } from 'redux-act';

const addTodo = createAction('ADD_TODO');
const removeTodo = createAction('REMOVE_TODO');
const editTodo = createAction('EDIT_TODO');
const completeTodo = createAction('COMPLETE_TODO');
const completeAll = createAction('COMPLETE_ALL');
const clearCompleted = createAction('CLEAR_COMPLETED');
import {
  addTodo, deleteTodo, editTodo, completeTodo,
  completeAll, clearCompleted
} from './actions';
import { createReducer } from 'redux-act';

const initialState = [
  {
    text: 'Use Redux',
    completed: false,
    id: 0
  }
]

export default createReducer({
  [addTodo]: (state, { text }) => [
    {
      id: state.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1,
      completed: false,
      text
    },
    ...state
  ],
  [deleteTodo]: (state, { id }) => state.filter(todo => todo.id !== id),
  [editTodo]: (state, { id, text }) => state.map(todo =>
    todo.id === id ?
      { ...todo, text } :
      todo
  ),
  [completeTodo]: (state, { id }) => state.map(todo =>
    todo.id === id ?
      { ...todo, completed: !todo.completed } :
      todo
  ),
  [completeAll]: (state) => {
    const areAllMarked = state.every(todo => todo.completed)
    return state.map(todo => ({
      ...todo,
      completed: !areAllMarked
    }))
  },
  [clearCompleted]: (state) => state.filter(todo => todo.completed === false)
}, initialState)

Actions

Reducer

export const ADD_TODO = 'ADD_TODO';
export const REMOVE_TODO = 'REMOVE_TODO';
export const EDIT_TODO = 'EDIT_TODO';
export const COMPLETE_TODO = 'COMPLETE_TODO';
export const COMPLETE_ALL = 'COMPLETE_ALL';
export const CLEAR_COMPLETED = 'CLEAR_COMPLETED';
import * as types from './constants';

const addTodo = (text) => ({
  type: types.ADD_TODO,
  payload: { text }
});

const removeTodo = (id) => ({
  type: types.REMOVE_TODO,
  payload: { id }
});

const editTodo = (id, text) => ({
  type: types.EDIT_TODO,
  payload: { id, text }
});

const completeTodo = (id) => ({
  type: types.COMPLETE_TODO,
  payload: { id }
});

const completeAll = () => ({
  type: types.COMPLETE_ALL
});

const clearCompleted = () => ({
  type: types.CLEAR_COMPLETED
});
import {
  ADD_TODO, DELETE_TODO, EDIT_TODO, COMPLETE_TODO,
  COMPLETE_ALL, CLEAR_COMPLETED
} from './constants';

const initialState = [
  {
    text: 'Use Redux',
    completed: false,
    id: 0
  }
]

export default function todos(state = initialState, action) {
  switch (action.type) {
    case ADD_TODO:
      return [
        {
          id: state.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1,
          completed: false,
          text: action.text
        },
        ...state
      ]

    case DELETE_TODO:
      return state.filter(todo =>
        todo.id !== action.id
      )

    case EDIT_TODO:
      return state.map(todo =>
        todo.id === action.id ?
          { ...todo, text: action.text } :
          todo
      )

    case COMPLETE_TODO:
      return state.map(todo =>
        todo.id === action.id ?
          { ...todo, completed: !todo.completed } :
          todo
      )

    case COMPLETE_ALL:
      const areAllMarked = state.every(todo => todo.completed)
      return state.map(todo => ({
        ...todo,
        completed: !areAllMarked
      }))

    case CLEAR_COMPLETED:
      return state.filter(todo => todo.completed === false)

    default:
      return state
  }
}

Constants

Actions

Reducer

redux-act

  • Reduce boilerplate
  • Use actions themselves as references rather than constants 
  • Flux Standard Action compliant

Honorable Mentions

redux-actions

Conclusion

  • Utilize "smart containers" and "dumb components"
  • Use Immutable data to help performance
  • A normalized state tree helps avoid duplication
  • Conquer side-effects with redux-thunk or redux-saga
  • Create performant, composable selectors with reselect
  • Use redux-act to reduce boilerplate and simplify

Links

Joel Kanzelmeyer

Questions?

Copy of Taming Large React Applications w/ Redux

By gerson231294

Copy of Taming Large React Applications w/ Redux

In this talk, Joel will introduce concepts that make large React applications more scalable and maintainable. You will learn the benefits of Redux a predictable, single-way data flow model as he walks through sample code and best practices like top down approach. You’ll walk away with what you need to know to architect a React application with all the patterns that help it scale well with your team. We will cover bootstrapping a modern React application with react-boilerplate (http://reactboilerplate.com) and using libraries like redux-saga and reselect to improve code re-use, removing coupling, and reducing complexity.

  • 579