27

This should be simple but I'm not finding the simple answer I want. I have a reducer:

const posts = (state = null, action) => {
  switch(action.type){
    case "PUBLISH_POST":
        return state;
    case "UNPUBLISH_POST":
        return state;
    default:
        return postList;
  }
}

I have a list of posts with ID's and a status. I'm sending in my post ID but can't figure out the logic to simply update the status from 0 to 1 for the item which has been clicked. I've found plenty of half-solutions but they all seem verbose and ugly - what's the shortest/best way of achieving it in this case?

Example data:

{
    id:1,
    user:"Bob Smith",
    content:"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vulputate mauris vitae diam euismod convallis. Donec dui est, suscipit at dui vitae, sagittis efficitur turpis. ",
    status:1 
}
3
  • How does you state look like? Is it an array of posts? Commented Oct 17, 2017 at 13:03
  • @mersocarlin yes, example above Commented Oct 17, 2017 at 13:04
  • What is the action payload? Commented Oct 17, 2017 at 13:09

4 Answers 4

34

Assuming your action is something like:

{
  type: 'UNPUBLISH_POST',
  payload: {
    id: 1,
    user: 'Bob Smith',
    content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vulputate mauris vitae diam euismod convallis. Donec dui est, suscipit at dui vitae, sagittis efficitur turpis. ',
    status: 1
  }
}

Simply use spread operator for it:

const posts = (state = null, action) => {
  switch(action.type){
    case "PUBLISH_POST":
    case "UNPUBLISH_POST":
        const index = this.state.findIndex(post => post.id === action.payload.id)

        return [
           ...state.slice(0, index), // everything before current post
           {
              ...state[index],
              status: action.type === 'PUBLISH_POST' ? 1 : 0,
           },
           ...state.slice(index + 1), // everything after current post
        ]
    default:
        return postList;
  }
}
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks. I had to remove "this." from the state. I realise I can not just do a toggle as well, rather than two conditions.
@MattSaunders you're welcome. I just noticed your state defaults to null. Maybe initialize it as an empty array so you don't have future problems with it :)
You might also consider using the immutability-helper module along with the pattern described here.
thankss for the slice method, really useful with redux ;)
Like me, in case someone else is wondering if state.slice(index + 1) will error out if index belongs to the last element in the array, the answer is no. The slice method simply returns an empty array ([]) if the index is out of bounds.
8

A more general solution, especially if state contains other data besides your posts array:

const posts = (state = null, action) => {
  const post = state.posts.find(p => p.id === action.payload.id);
  switch(action.type) {
    case "PUBLISH_POST":
      return { ...state, posts: [ ...state.posts.filter(p => p !== post), { ...post, status: 1 } ] };
    case "UNPUBLISH_POST":
      return { ...state, posts: [ ...state.posts.filter(p => p !== post), { ...post, status: 0 } ] };
    default:
      return state;
  }
}

4 Comments

I think you'd want to return filter(p => p !== post) and the post to update?
@KerSplosh You're absolutely right, thanks. Updated the answer according to your comment.
Won't this mutate the order of the posts array though?
@slavlazar Yes it will. If you need to preserve order then use mersocarlin's solution using slice.
3

Assuming you have an array of posts, you can search for the ID of the post you want to modify and then update it.

Your initial state:

const initial = {
   posts: [],
}

Your reducer:

case MODIFY_POST:
        return {
            ...state, //Returns the current state
            posts: state.posts.map(post=> post.id === action.id ? // Loop through the array to find the post you want to modify
                { ...post, status: action.status} : post // Copy the post state and then modify it. Else return the same object.
            )
        }

Now, in your actions you can have two actions to toggle the status:

export const enablePost= (post_id) => {
return {
    type: MODIFY_POST,
    id: post_id,
    status: 1
    }
}
export const disablePost= (post_id) => {
return {
    type: MODIFY_POST,
    id: post_id,
    status: 0
    }
}

Comments

1

You can just use the spread operator on your state and it will automatically update it the way you want.

example:

    import { GET_ALL_PENDING_DATAOPS, CHANGE_DATAOPS_ASSIGNED_TO } from "../Actions/types";
const initialState = {
  requests: null
};
export default function(state = initialState, action) {
  const { type, payload } = action;

  switch (type) {
    case GET_ALL_PENDING_DATAOPS:
      return {
        ...state,
        requests: payload
      };
      case CHANGE_DATAOPS_ASSIGNED_TO:          
        return {
          ...state,      
          requests:payload         
        }
    default:
      return state;
  }
}

My CHANGE_DATAOPS_ASSIGNED_TO action maps the previous state, in my axios call i am updating a single request object out of an array of requests, since it is returning a single object, and i am not explicitly returning that as just the state, I am using spread, it updates only that object and leaves the rest alone.

1 Comment

Thanks for adding an answer. Can you please include your code within the answer instead of as an image? Posting screenshots of code is bad practice on Stack Overflow.

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.