Home > Enterprise >  How to change reducer state change with the button click in Redux?
How to change reducer state change with the button click in Redux?

Time:11-05

I am trying to make a personal page with the name and other info and when you click EDIT PROFILE button it will take you to another page to edit the information.

On the EDIT Profile page I have the information on the input form that you can input new info. I want it to reflect the change only when I click the SAVE button. If I don't click the SAVE button and just click X button instead, it's not supposed to change when I go back the Profile page.

I am not sure what I did wrong but right now, it changes whether I clicked the SAVE button or not. Can someone please help me figure out what I did wrong? I've been at this for hours and I still can't figure out where I went wrong.

Below are my code:

profile-reducers.js

const profileReducer = (state = profile, action) => {
    switch (action.type) {
        case 'edit-profile':
            state[0].name = action.name;
            return state;

        default:
            return state;
    }
}


export default profileReducer;

edit-profile.js

const EditProfileItem = () => {
    let profile = useSelector(state => state.profile[0]);
    const [name, setname] = useState(profile.name);
    
    const dispatch = useDispatch();
    const editProfileClickHandler = () => {
        dispatch({
            type: 'edit-profile',
            name: name,      
        });
    }

    return (
      <>
        <div className = 'row p-2'>
            <div className = 'float-start col-1'>
                <Link to='/tuiter/profile'>
                    <i className = 'fas fa-times icon' style={{color:'black'}}/>
                </Link>
            </div>
            <div className='col-5 text-black fw-bold'>
                Edit Profile
            </div>
            <div className = 'col-6'>
                <Link
                    className='btn rounded-pill bg-black float-end text-white fw-bold'
                    to='/tuiter/profile'
                    onClick={editProfileClickHandler()}
                > Save
                </Link>
            </div>
        </div>

        <div className = 'pt-3'>
            <ul className='list-group'>
                <li className='list-group-item'>
                    <label className='text-secondary'>Name</label>
                    <input
                        className='form-control border-0 p-0'
                        defaultValue={profile.name}
                        onChange={(event) => setname(event.target.value)}/>
                </li>
            </ul>
       </div>
   </>
};
export default EditProfileItem;

CodePudding user response:

Issues

  • The EditProfileItem is rendering a link where the onClick handler's callback is immediately invoked when the component renders. It should be passed as a callback to be called later when actually clicked.

    onClick={editProfileClickHandler} instead of onClick={editProfileClickHandler()}

  • The profileReducer should mutate state or return a new state value, but not both.

  • The input should be fully controlled using the name state and the value prop instead of the defaultValue prop.

Solution

const EditProfileItem = () => {
  const profile = useSelector(state => state.profile[0]);
  const [name, setName] = useState(profile.name);
    
  const dispatch = useDispatch();

  const editProfileClickHandler = () => {
    dispatch({
      type: 'edit-profile',
      payload: name,      
    });
  };

  return (
    <>
      <div className='row p-2'>
        <div className='float-start col-1'>
          <Link to='/tuiter/profile'>
            <i className='fas fa-times icon' style={{ color: 'black' }} />
          </Link>
        </div>
        <div className='col-5 text-black fw-bold'>
          Edit Profile
        </div>
        <div className='col-6'>
          <Link
            className='btn rounded-pill bg-black float-end text-white fw-bold'
            to='/tuiter/profile'
            onClick={editProfileClickHandler}   // <-- pass callback reference
          >
            Save
          </Link>
        </div>
      </div>

      <div className = 'pt-3'>
        <ul className='list-group'>
          <li className='list-group-item'>
            <label className='text-secondary'>Name</label>
            <input
              className='form-control border-0 p-0'
              value={name}                     // <-- use value prop
              onChange={(event) => setName(event.target.value)}
            />
          </li>
        </ul>
      </div>
    </>
  );
};

profileReducer

Since this doesn't appear to be a redux-toolkit slice reducer I'll assume it is a legacy redux reducer function. The previous state should be shallow copied, and then the appropriate property updated.

const profileReducer = (state = profile, action) => {
  switch (action.type) {
    case 'edit-profile':
      const nextState = state.slice();
      nextState[0] = {
        ...nextState[0],
        name: action.payload
      };
      return nextState;

    default:
      return state;
  }
};

CodePudding user response:

onClick={editProfileClickHandler()}

This particular line, you're calling the function on every render, you need to pass a function into an onClick handler, and not the return of a function

it should be onClick={()=> editProfileClickHandler()}

here a function is being passed into the onClick, which would be called when there is a click event

i hope this helps

  • Related