Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding Redux to an existing React app

I've been working on a React app and have gotten to a point where I'll need Redux to handle some aspects of it.

After reading a bunch of tutorials, I'm fairly stuck on how to make my "smarter" components "dumber" and move functions into my actions and reducers.

So, for example, one aspect of the app is more of a to-do list style.

One of my classes starts like this:

export default class ItemList extends React.Component {
  constructor() {
    super();
    this.state = { items: [],
                   completed: [],
                  };
    this.addItem = this.addItem.bind(this);
    this.completeItem = this.completeItem.bind(this);
    this.deleteItem = this.deleteItem.bind(this);
  }

  addItem(e) {
    var i = this.state.items;
    i.push({
      text: this._inputElement.value,
      paused: false,
      key: Date.now()
    });
    this.setState({ items: i });
    e.preventDefault();
    this._inputElement.value = '';
    this._inputElement.focus();
  }

  completeItem(e) {
    this.deleteItem(e);
    var c = this.state.completed;
    c.push({
      text: e.target.parentNode.parentNode.getElementsByClassName('item-name')[0].innerHTML,
      paused: false,
      key: Date.now()
    });
    this.setState({ completed: c });
  }

  deleteItem(e) {
    var i = this.state.items;
    var result = i.filter(function(obj) {
        return obj.text !== e.target.parentNode.parentNode.getElementsByClassName('item-name')[0].innerHTML;
    });
    this.setState({ items: result });
  }

  // ... more irrelevant code here ...

  // there's a function called createTasks that renders individual items

  render() {
    var listItems = this.state.items.map(this.createTasks);

    return <div className="item-list">
      <form className="form" onSubmit={this.addItem}>
        <input ref={(a) => this._inputElement = a}
               placeholder="Add new item"
               autoFocus />
        <button type="submit"></button>
      </form>

      {listItems}
    </div>;
  }
}

So, as you can see, it's very logic-heavy. I've started adding Redux by adding a <Provider> in my index file, and made a basic reducers file that is fairly empty so far:

import { combineReducers } from 'redux';

const itemList = (state = {}, action) => {

};

// ... other irrelevant reducers

const rootReducer = combineReducers({
  itemList,
  // ...
});

export default rootReducer;

...and I've made an actions file that doesn't have much in it yet either.

I've been struggling to figure out:

  • Most actions I've seen examples of just return some kind of JSON, what do I return in the reducer that uses that JSON that my component can use?
  • How much of my component logic is reusable, or should I just forget it? What is the best way to go about this to reuse as much code as I've written as possible?
like image 963
Cassidy Avatar asked Apr 13 '17 00:04

Cassidy


1 Answers

First of all you need to understand the overall picture of how redux works with react.

Before coming to that lets first understand what are smart components and dumb components.

Smart Components

  1. All your code logic needs to be handled here
  2. They are also called containers.
  3. They interact with the store(aka state management) to update your components.

Dumb Components

  1. They just read props from your containers and render you components
  2. This is just the UI view and should not contain any logic.
  3. All styling/html/css comes in your dumb components.

Here is an amazing piece of article which you can go through to understand smart and dumb components if you still have doubts.

Ok, now lets try understanding how redux works:-

  • Your smart components(aka containers) interact with your redux store
  • You fire actions from your containers.
  • Your actions call your apis
  • The result of your action updates the store through a reducer
  • You containers read the store through mapStateToProps function and as soon as value in store changes it updates your component.

Now lets consider your todo example

TodoListContainer.js

class TodoListContainer extends Component {

  componentWillMount () {
    // fire you action action
  }


  render () {
    return (
      <Todos todos=={this.props.todos} />
    )
  }
}


function mapStateToProps(state) {
  const {todos} = state;
  return {
    todos;
  }
}

export default connect(mapStateToProps)(TodoListContainer)

TodoList.js

class TodoList extends Component {

  renderTodos() {
    return this.props.todos.map((todo)=>{
      return <Todo todo={todo} key={todo.id} />
    })
  }

  render () {
    return () {
      if (this.props.todos.length === 0) {
        return <div>No todos</div>
      }
      return (
        <div>
          {this.renderTodos()}
        </div>
      )
    }
  }
}

export default class TodoList

Todo.js

class Todo extends Component {

  render () {
    return (
      <div>
        <span>{this.props.todo.id}</span>
        <span>{this.props.todo.name}</span>
      </div>
    )
  }
}

Reducer

export default function todos(state={},action) {
  switch (action.type) {
    case 'RECEIVE_TODOS':
       return Object.assign(state,action.todos);
  }
}

action

function fetchTodos() {
  return(dispatch) => {
      axios.get({
    //api details
    })
    .then((res)=>{
      dispatch(receiveTodos(res.todos))
    })
    .catch((err)=>{
      console.warn(err)
    })
  }
}

function receiveTodos(todos) {
  return {
    type: 'RECEIVE_TODOS',
    todos
  }
}

Now if you have read redux documentation you would see that actions return objects then how would i call my api there which returns a function instead of an object. For that I used redux thunk about which you can read here.

I gave you an example in which you can fetch todos. If you want to do other operations like deleteTodo, addTodo, modifyTodo then you can do that in appropriate components.

  1. DeleteTodo - you can do in TodoListContainer.
  2. AddingTodo - you can do in TodoListContainer.
  3. Changing State(completed/Pending) - you can do in TodoListContainer.
  4. ModifyingTodo - you can do in TodoContainer.

You can also check out here for a detailed example, but before that I would say just should go through basics of redux which you can find here

P.S: I wrote code on the fly so it might not work properly but it should work with little modification.

like image 159
Harkirat Saluja Avatar answered Oct 19 '22 11:10

Harkirat Saluja