1

I'm trying to learn the React / Redux and build the simple app by using .Net core Api as the backend service.

My Requirements

  1. Add item when the user clicks Save button

  2. Redirect to the List page and load the data again to retrieve the newly added data

  3. I have to reload the data and cannot just append the new item in the list because I need to get the keyId of the newly added item which is just generated

So, I do the chaining of promises in my action file to call LoadItems after AddItem is successful.

If I don't chain these promises, I could not see the newly created item in the List page. The redirection to '/todo' occurs so quickly even before the AddItem() is not completed yet. If I added 2 seconds delay for the redirection, I could see the new item.

Actions

export const addTodoItemSuccess = todoItem => ({ type: actionTypes.ADD_TODO_ITEM_SUCCESS, payload: todoItem });

export const loadTodoItemsSuccess = items => ({ type: actionTypes.LOAD_TODO_SUCCESS, payload: items });

export const loadTodoItems = () => {
    return function (dispatch) {
        return TodoService.getAll().then(items => {
            dispatch(loadTodoItemsSuccess(items));
        });
    };
}

export const addTodoItem = (item) => {
    return function (dispatch) {
        return TodoService.add(item).then(() => {
            return TodoService.getAll().then(items => {
                dispatch(loadTodoItemsSuccess(items));
            });
        });
    };
}

Reducer

import * as actionTypes from '../actions/actionTypes';

const todoReducer = (state = [], action) => {
    switch (action.type) {
        case actionTypes.LOAD_TODO_SUCCESS:
            return action.payload;
        default:
            return state;
    }
}

export default todoReducer;

AddTodoPage Container

submitNewTodo = event => {
        event.preventDefault();     
        this.props.addTodoItem(this.state.item);

        //redirect to Todo List Page after saving
        this.context.router.history.push(`/todo`); 
    }

TodoListPage Container

componentDidMount = () => {
        this.props.dispatch(loadTodoItems());
    }

It works as expected and I can see the new item in the list. But the problem is that it's sending TWO GetAll() queries to the Api. First call comes from Actions.js and Second call comes from componentDidMount in TodoListPage.js.

enter image description here

If I removed the loadTodoItems() from the componentDidMount, I could not view any items when I just navigate to TodoListPage '/todo' because the items are not loaded yet.

In tutorials, they usually do store.dispatch(loadTodoItems()); in index.js to make it available. I feel wrong to load the data even before the user hasn't navigated to that page (except lookup data).

Could you please suggest me what's the best way to achieve my requirements above? I don't want to call the Api twice to refresh the data.

The complete set of code can be found here: https://github.com/ttcg/react-redux-todo/tree/todocrud/src

1 Answer 1

1

I managed to sort out this problem in the following way.

The main problem is that I was trying to develop the redirection in the traditional way rather than React-Redux way.

Normally, we do the redirection to another route after a button click or some actions because we assume that it won't execute the redirection until the execution of the action has completed.

However, Javascript execution is async and they don't wait the completion of the previous line. So, it will always redirect without waiting for the previous action. So many tutorials online are using that way. It might work for the tutorials because there is no delay and they are manipulating the objects in the memory.

Wrong way to redirect after button click

submitNewTodo = event => {
    event.preventDefault();     
    this.props.addTodoItem(this.state.item);

    //redirect to Todo List Page after saving
    this.context.router.history.push(`/todo`); 
}

But in React-Redux, the data flow is Unidirectional and we need to implement our codes with that flow in mind.

Changed in Reducer to return the mark addTodoSuccess value

const todoReducer = (state = initialState, action) => {
    switch (action.type) {
        case actionTypes.ADD_TODO_ITEM_SUCCESS: {
            return { ...state, addTodoSuccess: true };
        }
        ...
        default:
            return state;
    }
}

Map the value in the AddTodoPage container to catch the flag

const mapStateToProps = (state) => {
    let item = { id: uuidv4(), taskItem: '', doneBy: '' };

    return {
        item: item,
        addTodoSuccess: state.todo.addTodoSuccess
    };
}

Do the redirection in the Render() method to check the value returned by reducer

{
addTodoSuccess
? (<Redirect to={"/todo"} />)
:
    <Container>
        <h4>Add New Todo</h4>
        ....
    </Container>
}

Notes:

  1. It can be solved by using Promises but it breaks the Redux flow of UniDirectional.

  2. You do not have to use Redux for this kind of feature. But, I'm learning Redux and trying to follow the tutorials.

  3. Some people might do the redirection in Actions / Reducers. There are debates about that way too.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.