在达到状态之前在Redux Reducer中设置日期格式

Format date in Redux Reducer before it hits state

本文关键字:设置 日期 格式 Reducer 状态 Redux      更新时间:2023-09-26

如何从标准JS日期对象中格式化日期,该对象来自服务器,并通过RESTAPI通过Redux输入React,其初始输出为:

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

换成不同的格式?所以它被列为DD MM YYYY

我可以在Reducer of Action中通过操纵请求结果或有效载荷来做到这一点吗:

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;
}

带动作:

export function fetchBooks() {
    const request = axios.get('/api/books');
    return {
        type: FETCH_BOOKS,
        payload: request
    }
}

在Books React组件的componentWillMount()方法中调用fetchBooks()

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

api返回以下结构化JSON:

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

更新

非常感谢Nicole和nrabinowitz-我已经实施了以下更改。。。在动作创造者

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 
    }
};

我正在transform-date函数中记录book对象,它正在向对象的根添加iddate,但json的结构实际上需要是book.data.idbook.data.date。我不知道如何在transformDate函数中创建正确的结构以返回给动作创建者。

这是转换日期功能控制台日志中的图书对象:

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

我的函数需要将id和日期放在数据对象中。(日期只是标准的日期格式,因为我的moment实现有问题(date: new Date(moment(json.data.date).format('yyyy-MM-dd'))返回Invalid date)。

再次感谢你的帮助,真的很感激。

当数据到达前端时,即在您的AJAX请求中,我会立即执行此转换。这样,您的前端应用程序只在您希望对数据进行整形时使用数据,而您的请求处理程序在服务器提供数据结构时封装并隐藏数据结构,并以所需的形状将数据传递给前端应用程序。

(在领域驱动设计中,这被称为"反腐败层")。

关于技术解决方案:

目前,您的事件负载包含对数据的承诺。您没有向我们显示您在哪里调用fetchBooks(),即在哪里执行承诺。这就是您需要进行数据转换的地方。(但就我个人而言,我宁愿将数据转换保留在fetchBooks函数中。)

此外,这也是您需要将数据发送到redux存储的地方。为了能够异步减少事件,您需要类似redux-thunk的东西。

解决方案可能是这样的(很抱歉,我在使用promise方面不是很有经验,所以我会在这里使用回调,但也许你可以以某种方式将其转换为promise):

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

transformData应该完成从后端格式到前端格式的数据转换。

在其他地方,您可能会触发一个发出的动作来获取书籍(现在我们称之为loadBooks)。这就是您需要进行异步调度的地方:

function loadBooks() {
    return (dispatch, getState) => {
        fetchBooks(response => {
            dispatch(receiveBooks(response));
        });
    };
}
function receiveBooks(data) {
    return {
        type: FETCH_BOOKS,
        payload: data
    };
}

这样,只有在AJAX调用实际返回数据后,您才会将数据分派到redux存储。

要使用redux-thunk,您需要将其中间件注入到您的商店中,如下所示:

import thunkMiddleware from "redux-thunk";
import { createStore, applyMiddleware } from "redux";
const createStoreWithMiddleware = applyMiddleware(
    thunkMiddleware
)(createStore);

然后将减速器函数传递到此存储。

如果您还有其他问题,请继续提问。

我同意@Nicole的观点——这最好在AJAX客户端中处理。我在这里经常使用的一种模式是DataMapper,类似于:

function jsonToBook(json) {
    // do whatever transformations you need here
    var book = {
        ...json,
        id: json._id,
        date: new Date(json.date)
    }
    // return the transformed, app-appropriate object
    return book;
}

然后,在AJAX客户端中,您可以将转换转换为.then调用:

const request = axios.get('/api/books')
    .then(jsonToBook);

这仍然返回一个promise,所以您的promise中间件应该仍然可以按预期工作。这样做的一个优点是,数据映射器确实很容易测试,但AJAX客户端本身的逻辑更难。

这里的另一点是,我建议解析Date对象,而不是格式化的字符串。这是意见,但总的来说,我认为格式化问题是视图的领域(即React组件),而不是存储/还原器。例如,传递Date而不是字符串可以更容易地在应用程序的不同部分以不同的方式设置日期格式。

有关实际日期格式,请参阅Moment.js或D3的时间格式实用程序。

与其在收到响应后修改响应,不如使用axios库的transformResponse函数作为[axios docs][1]和[example][2]调用的一部分来解决问题:

const ISO_8601 = /('d{4}-'d{2}-'d{2})T('d{2}:'d{2}:'d{2}.'d{3})Z/;
export function fetchBooks() {
   const request = axios.get('/api/books', {
      transformResponse: axios.defaults.transformResponse.concat(function (data, headers) {
        Object.keys(data).forEach(function (k) {
          if (ISO_8601.test(data[k])) {
            data[k] = new Date(Date.parse(data[k]));
          }
        });
        return data;
      })
    });
  return {
    type: FETCH_BOOKS,
    payload: request
  }
}

如果您希望在整个应用程序中以对象的形式一致地返回日期,那么可以使用拦截器。请注意,以下示例只会将顶级对象从字符串更新为日期,而不是嵌套对象:

axios.interceptors.response.use(function (response) {
  // Identify data fields returning from server as ISO_8601 formatted dates
  var ISO_8601 = /('d{4}-'d{2}-'d{2})T('d{2}:'d{2}:'d{2}.'d{3})Z/;
  var data = response.data
  Object.keys(data).forEach(function (k) {
    if (ISO_8601.test(data[k])) {
      data[k] = new Date(Date.parse(data[k]));
    }
  });
  return response;
}, function (error) {
  // Do something with response error
  return Promise.reject(error);
});

serify-deserify将不可序列化的值可逆地转换为可序列化的,然后再转换回来。它本机支持BigIntDateMapSet,您可以轻松添加对自定义类型的支持。

该包包括一个Redux中间件,该中间件在进入商店的过程中应用串行化转换。在deserify函数中包装检索函数,以便将结果转换为适当的类型。

全面披露:我是软件包的创建者!