ykokw.xyz blog logo

Blog Posts

No results for 'undefined'Powered by Algolia

reduxのstateをnormalizeされた状態で利用するときに、 データ更新があった場合どうするかの話

もやもやしていたら公式ドキュメント見つけた

ブログのpostにcommentが追加された場合の例を話している

1. シンプルにmergeする

import merge from "lodash/merge";

function commentsById(state = {}, action) {
    switch(action.type) {
        default : {
           if(action.entities && action.entities.comments) {
               return merge({}, state, action.entities.comments.byId);
           }
           return state;
        }
    }
}
  • stateとaction.entitiesをlodashのmergeで統合する方法
  • もしこの方法でnestしているデータを扱おうとすると、action.entitiesを用意するのが大変

    • commentのreducer側で、postIdをベースに更新対象のエンティティを取得することになる

2. reducerを分ける

  • postの更新とcommentの更新を別アクションにする
// actions.js
function addComment(postId, commentText) {
    // Generate a unique ID for this comment
    const commentId = generateId("comment");

    return {
        type : "ADD_COMMENT",
        payload : {
            postId,
            commentId,
            commentText
        }
    };
}


// reducers/posts.js
function addComment(state, action) {
    const {payload} = action;
    const {postId, commentId} = payload;

    // Look up the correct post, to simplify the rest of the code
    const post = state[postId];

    return {
        ...state,
        // Update our Post object with a new "comments" array
        [postId] : {
             ...post,
             comments : post.comments.concat(commentId)             
        }
    };
}

function postsById(state = {}, action) {
    switch(action.type) {
        case "ADD_COMMENT" : return addComment(state, action);
        default : return state;
    }
}

function allPosts(state = [], action) {
    // omitted - no work to be done for this example
}

const postsReducer = combineReducers({
    byId : postsById,
    allIds : allPosts
});


// reducers/comments.js
function addCommentEntry(state, action) {
    const {payload} = action;
    const {commentId, commentText} = payload;

    // Create our new Comment object
    const comment = {id : commentId, text : commentText};

    // Insert the new Comment object into the updated lookup table
    return {
        ...state,
        [commentId] : comment
    };
}

function commentsById(state = {}, action) {
    switch(action.type) {
        case "ADD_COMMENT" : return addCommentEntry(state, action);
        default : return state;
    }
}


function addCommentId(state, action) {
    const {payload} = action;
    const {commentId} = payload;
    // Just append the new Comment's ID to the list of all IDs
    return state.concat(commentId);
}

function allComments(state = [], action) {
    switch(action.type) {
        case "ADD_COMMENT" : return addCommentId(state, action);
        default : return state;
    }
}

const commentsReducer = combineReducers({
    byId : commentsById,
    allIds : allComments
});

3. feature単位のreducerでまとめて処理

  • コメントを追加する というfeature単位でreducerを作成

    • コメントが追加されるとどこに影響があるのかひと目で分かる
    • これも結局どうnestしているかをreducerが知っておく必要がある
import posts from "./postsReducer";
import comments from "./commentsReducer";
import dotProp from "dot-prop-immutable";
import {combineReducers} from "redux";
import reduceReducers from "reduce-reducers";

const combinedReducer = combineReducers({
    posts,
    comments
});


function addComment(state, action) {
    const {payload} = action;
    const {postId, commentId, commentText} = payload;

    // State here is the entire combined state
    const updatedWithPostState = dotProp.set(
        state, 
        `posts.byId.${postId}.comments`, 
        comments => comments.concat(commentId)
    );

    const updatedWithCommentsTable = dotProp.set(
        updatedWithPostState, 
        `comments.byId.${commentId}`,
        {id : commentId, text : commentText}
    );

    const updatedWithCommentsList = dotProp.set(
        updatedWithCommentsTable,
        `comments.allIds`,
        allIds => allIds.concat(commentId);
    );

    return updatedWithCommentsList;
}

const featureReducers = createReducer({}, {
    ADD_COMMENT : addComment,
};

const rootReducer = reduceReducers(
    combinedReducer,
    featureReducers
);

4. redux-orm を使う

  • あらかじめModelでnest構造とreducerなどを定義しておく
  • 内部でcombineされたreducerがいい感じにcommentとpostのインスタンスを更新する

その他

  • 2が知りたかったので、3 / 4は雑なメモになっているけど、いつか参考にしたい

    • 特にredux-orm
  • action typeが同じであれば、postとcommentも対応するactionがディスパッチされる

    • action typeの設定の仕方もpointになってくる

This content is built with Gatsby