ko_ma
ko_ma

Reputation: 1021

React: I have some error with edit state from reducers

My Source: https://codesandbox.io/s/x91zl9v78p

I try to edit title and content, but first time, content is not modified, only title is modified.

From the second it works well.

Why this error occurs?

reducers.js :

case UPDATE_POST:
  return state.map(
    post => (post.id === action.id ? Object.assign({}, post, action) : post)
  );

And, Is there a way I want to use the spread operator without using object.assign?

Upvotes: 0

Views: 86

Answers (4)

Raj Kumar N
Raj Kumar N

Reputation: 793

Because in item.jsx file this.state.title and this.state.content are updated only when input get's changed if either of the input is not changed then either of this.state.title or this.state.content are empty strings. After adding an item to the list this.props.title and this.props.content are available but this.state.title and this.state.content initially are defined as "" strings in the component. so insted of taking defaultvalue as this.props.tile and this.props.content, on edit set this.state.title = this.props.title and this.state.content = this.props.content then it will work.

Upvotes: 1

Marco Rojas
Marco Rojas

Reputation: 452

This occurs because your Item component's constructs with an empty string, so when you edit the title input the state is updated, and if you do not edit the content input it will remain empty.

components/List/items.jsx

class Item extends Component {
  constructor(props) {
    super(props);

    this.state = {
      isEdit: false,
      title: this.props.title,
      content: this.props.content
    };
  }
  //...
}

And to avoid Object.assign

reducers/post.jsx

case UPDATE_POST:
  return state.map(
    post => (post.id === action.id ? { ...post, ...action } : post)
  );

This will clone post props, and merge action props.

https://codesandbox.io/s/5xpnm9yrrx

Upvotes: 2

Yury Tarabanko
Yury Tarabanko

Reputation: 45106

Since you are storing initial values for title and content in props you need to sync initical Item component state values to props by implementing

static getDerivedStateFromProps({ title, content }) {
    return {
      title, content
    }
}

Working demo

Otherwise, if user doesn't touch a field (say for content) the code will use default state value which is an empty string.

And, Is there a way I want to use the spread operator without using object.assign?

case UPDATE_POST:
  return state.map(
    post => (post.id === action.id ? {...post, ...action} : post)
  );

Also by spreading entire action into the post you are polluting post object with additional attribute type. Consider using payload object instead to pass updated post.

export function updatePost(id, title, content) {
  return {
    type: UPDATE_POST,
    payload: {
      id,
      title,
      content
    }
  };
}

and then spread the payload

case UPDATE_POST:
  return state.map(
    post => (post.id === action.payload.id ? {...post, ...action.payload} : post)
  );

Upvotes: 1

Jayffe
Jayffe

Reputation: 1279

Edit wqr4r0wnvw

maybe by using it in the function args like this :

export default function Post(state = initialState, {type, ...post}) {

  switch (type) {

    case ADD_POST:
      return [ ...state, post ];

    case REMOVE_POST:
      return state.filter(({ id }) => id !== post.id);

    case UPDATE_POST:
      return state.map( p => (p.id === post.id ? post : p) );

    default:
      return state;
  }
}

Upvotes: 0

Related Questions