Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Format date in Redux Reducer before it hits state

how can I format a date from a standard JS date object, which has come from the server and is fed into React via Redux through a REST api, which has an initial output of:

"2016-05-16T13:07:00.000Z"

Into a different format? So it is listed as DD MM YYYY?

Can I do this in the Reducer of Action by manipulating the request result, or the payload:

import { FETCH_BOOKS, FETCH_BOOK } from '../actions';

const INITIAL_STATE = { books: [], book: null };

export default function(state = INITIAL_STATE, action) {
    switch(action.type) {
        case FETCH_BOOK:
            return { ...state, book: action.payload.data };
        case FETCH_BOOKS:
            return { ...state, books: action.payload.data };
    }
    return state;
}

With action:

export function fetchBooks() {
    const request = axios.get('/api/books');

    return {
        type: FETCH_BOOKS,
        payload: request
    }
}

fetchBooks() is called in the componentWillMount() method of the Books React component:

componentWillMount() {
    this.props.fetchBooks();
}

The api returns the following structured JSON:

[{"_id":0,"date":"2016-05-16T13:07:00.000Z","title":"Eloquent JavaScript","description":"Learn JavaScript"}]

Update

Thank you very much Nicole and nrabinowitz - I have implemented the following changes... in the action creators

function transformDateFormat(json) {
    const book = {
        ...json,
        id: json.data._id,
        // date: new Date(moment(json.data.date).format('yyyy-MM-dd'))
        date: new Date(json.data.date)
    }
    console.log(book);
    return book;
}

export function fetchBook(id) {
    const request = axios.get(`/api/books/${id}`)
        .then(transformDateFormat);

    return {
        type: FETCH_BOOK,
        payload: request 
    }
};

I am logging the book object in the transform date function, and it is adding id and date to the root of the object, but the structure of the json actually needs to be book.data.id and book.data.date. I'm not sure how to create the correct structure within the transformDate function to return to the action creator.

This is the book object from the transform date function console log:

Object {data: Object, status: 200, statusText: "OK", headers: Object, config: Object…}
config: Object
data: Object
    __v:0
    _id: "5749f1e0c373602b0e000001"
    date: "2016-05-28T00:00:00.000Z"
    name:"Eloquent JavaScript"
    __proto__:Object
date: Sat May 28 2016 01:00:00 GMT+0100 (BST)
headers: Object
id: "5749f1e0c373602b0e000001"
request: XMLHttpRequest
status: 200
statusText: "OK"
__proto__: Object

My function needs to place the id and date inside the data object. (The date is just standard date format as something is wrong with my implementation of moment (date: new Date(moment(json.data.date).format('yyyy-MM-dd')) returns Invalid date).

Thanks again for all your help - really appreciated.

like image 763
Le Moi Avatar asked May 16 '16 12:05

Le Moi


People also ask

Can Redux store dates?

It natively supports BigInt , Date , Map , and Set , and you can easily add support for custom types. The package includes a Redux middleware that applies the serify transformation on the way into the store. Wrap retrieval functions in the deserify function in order to transform the results to the appropriate type.

How do I change the initial state in Redux Reducer?

There are two main ways to initialize state for your application. The createStore method can accept an optional preloadedState value as its second argument. Reducers can also specify an initial value by looking for an incoming state argument that is undefined , and returning the value they'd like to use as a default.

Does Redux state reset on refresh?

Let's learn how to use Redux Persist to save the state in persistent storage so that even after a refresh, the data will still remain intact. We'll also learn how to customize what's persisted and specify how incoming states will be merged.

What is a reducer in Redux?

Reducers are functions that take the current state and an action as arguments, and return a new state result. In other words, (state, action) => newState. A Redux app really only has one reducer function: the "root reducer" function that you will pass to createStore later on.

How to modify the state of a redux store?

The state that we use inside redux as a store can only be modified with the help of the actions. But, this state must be specified somewhere first to use it. We prefer to declare our initial state at the reducer files. Then we use that reducer in the store and provide that store use inside the whole application.

Does date in Redux store change over time?

@markerikson - Please let me know if you think it would be helpful if I created and shared a small project reproducing our behavior of using Date in the store to further explore this. , I've mutated it, and there's no evidence that it changes. One of the core principles of Redux is to avoid mutating state.

What is the use of Redux in react?

In ReactJS, redux is the tool for the management of the state throughout the whole application globally. The state that we use inside redux as a store can only be modified with the help of the actions. But, this state must be specified somewhere first to use it. We prefer to declare our initial state at the reducer files.


1 Answers

I would do this transformation immediately when the data arrives in the frontend, i.e. in your AJAX request. This way, your frontend application only works with the data as you intend them to be shaped, and your request handler encapsulates and hides the data structures as they are provided from the server and passes the data to the frontend application in the desired shape.

(In Domain-Driven Design, this goes under the name of "Anticorruption Layer").

Regarding the technical solution:

Currently, your event payload contains a promise to the data. You don't show us where you invoke fetchBooks(), i.e. where the promise is executed. That's where you need to do the data transformation. (But personally, I'd rather keep the data transformation inside the fetchBooks function.)

Also, that's the point where you need to send the data to the redux store. To be able to reduce events asynchronously, you need something like redux-thunk.

A solution might look like this (my apologies, I'm not very experienced in using promises, so I'll use a callback here, but maybe you can somehow translate that to a promise):

export function fetchBooks(callback) {
    const request = axios.get('/api/books')
                    .then(function (response) {
                       callback(transformData(response));
                    });
}

transformData is supposed to do the date transformation from backend format to frontend format.

Somewhere else you probably trigger an action that gets emitted in order to fetch the books (let's call it loadBooks for now). That's where you need to do the asynchronous dispatch:

function loadBooks() {
    return (dispatch, getState) => {

        fetchBooks(response => {
            dispatch(receiveBooks(response));
        });
    };
}

function receiveBooks(data) {
    return {
        type: FETCH_BOOKS,
        payload: data
    };
}

This way, you will only dispatch the data to the redux store once it was actually returned from the AJAX call.

To use redux-thunk, you need to inject its middleware into your store like this:

import thunkMiddleware from "redux-thunk";
import { createStore, applyMiddleware } from "redux";

const createStoreWithMiddleware = applyMiddleware(
    thunkMiddleware
)(createStore);

and then pass your reducers function to this store.

Please keep asking if you have any further questions.

like image 161
Nicole Avatar answered Sep 27 '22 20:09

Nicole