Home > other >  Remove row from React table without refreshing page
Remove row from React table without refreshing page

Time:07-28

I have a React table which has a column containing delete buttons in each row. When a user clicks on the delete button, a modal pops up asking them to confirm deletion. Once user clicks confirm, the modal closes and the row should be deleted from the table.

I am trying to make it so that I don't have to refresh the page to see the updated table and instead see it right away once the confirm button in the modal is clicked and it closes.

Update: I tried the solution from the comments but I'm getting an error that says Uncaught TypeError: Cannot destructure property 'deleteCriteria' of '_ref' as it is undefined. How would I fix this error? I'm very new with react so I'd appreciate any help!

This is my index.js file:

const DataTable = () => {
  const dispatch = useDispatch();
  const criterias = useSelector((state) => state.questionnaireData.criterias);

  useEffect(() => {
      if(criterias.length === 0) {
          dispatch(fetchCriteria());
      }
  },[dispatch, criterias])

  const mapCriterias = criterias.map((criterias) => ({
    id: criterias.criteriaid,
    criteria: criterias.criteria
  }));

  return (
    <>
      <table>
        <tbody>
          {criterias.map(criteria=> {
            return (
              <tr key={criteria.criteriaid}>
                <td>{ criteria.id }</td>
                <td>{ criteria.criteria }</td>
                <td><DeleteButton deleteCriteria={dispatch(deleteCriteriaById)} id={criteria.criteriaid} /></td>
              </tr>
            )
        </tbody>
      </table>
    </>
  )
}

My renderDeleteButton (also in index.js):

export const DeleteButton = ({deleteCriteria, id}) => {
  let dialogEl=null;

  const delete_question = async () => {
    try {
      const response = await axios.delete(`http://localhost:3001/criteria/${encodeURIComponent(id)}`);
      deleteCriteria(id);
    } catch (err) {
      console.error("delete_question", err.toJSON());
    }
  }

  return (
    <>
      // modal
      <dialog ref={(el) => {
        dialogEl = el;
      }}>

        <div role="document">
          <h2>Are you sure you would like to delete this food?</h2>
          <p>This action cannot be undone</p>
          <form method="dialog">
            <div>
              <div>
                <button type="reset" onClick={()=>dialogEl.close()}>Cancel</button>
              </div>
              <div>
                <button type="del" id="delete_bottom" onClick {()=>delete_question()}>Delete</button>
              </div>
            </div>
          </form>
        </div>
      </dialog> 

      // delete button
      <button onClick={() =>dialogEl.showModal()} className="delete-btn">
        <span role="img">
          <Icon icon="gg:trash-empty"/>
        </span>
      </button>
    </>
  )
}

My questionnaire.js file:

const initialState = {
  criterias: [],
  isFetching: false
}

const QuestionnaireSlice = createSlice({
  name: "QUESTIONNNAIRE",
  initialState,
  reducers: {
    fetchCriteria: (state) => ({...state, isFetching: true}),
    confirmFetchCriteria: (state) => ({ ...state, isFetching: false }),
    setCriteria: (state, action) => ({ ...state, criterias: action.payload }),
    deleteCriteriaById: (state, action) => ({ ...state, criterias: state.criterias.filter(({criteriaid}) => criteriaid !== action.payload) })
  }
})

CodePudding user response:

I think there may be a few optimizations you could make to help achieve what you want.

First I think you may be unnecessarily iterating over the criterias array twice - you should be able to just map once while rendering in your JSX return statement.

Second, your renderDeleteButton function looks like it wants to be a react component (it's returning JSX). So you'll want to implement this a little bit differently to allow the component to take in a delete handler and criteria ID as props.

The delete handler should be a dispatch function that updates your state with the new criterias array that does not include the one with that criteria id.

Something like:

const DeleteButton = ({id}) => {
    const dispatch = useDispatch();
  let dialogEl=null;

  const delete_question = async () => {
    try {
      const response = await axios.delete(`http://localhost:3001/criteria/${encodeURIComponent(id)}`);
      dispatch(deleteCriteriaById(id));
    } catch (err) {
      console.error("delete_question", err.toJSON());
    }
  }

  return (
    <>
      // modal
      <dialog ref={(el) => {
        dialogEl = el;
      }}>

        <div role="document">
          <h2>Are you sure you would like to delete this food?</h2>
          <p>This action cannot be undone</p>
          <form method="dialog">
            <div>
              <div>
                <button type="reset" onClick={()=>dialogEl.close()}>Cancel</button>
              </div>
              <div>
                <button type="del" id="delete_bottom" onClick={delete_question}>Delete</button>
              </div>
            </div>
          </form>
        </div>
      </dialog> 

      // delete button
      <button onClick={() =>dialogEl.showModal()} className="delete-btn">
        <span role="img">
          <Icon icon="gg:trash-empty"/>
        </span>
      </button>
    </>
  )
}

const DataTable = () => {
  const dispatch = useDispatch();
  const criterias = useSelector((state) => state.criterias);

  useEffect(() => {
      if(criterias.length === 0) {
          dispatch(fetchCriteria());
      }
  },[dispatch, criterias])

  return (
    <>
      <table>
        <tbody>
          {criterias.map(criteria=> {
            return (
              <tr key={criteria.criteriaid}>
                <td>{ criteria.criteriaid }</td>
                <td>{ criteria.criteria }</td>
                <td><DeleteButton id={criteria.criteriaid} /></td>
              </tr>
            )})}
        </tbody>
      </table>
    </>
  )
}

    

deleteCriteriaById could probably look something like:

deleteCriteriaById: (state, action) => ({...state, 
  criterias: state.criterias.filter(({criteriaid}) => criteriaid !== action.payload)
}),

CodePudding user response:

Just change window.location.reload(false); with dispatch(fetchCriteria()); assuming that the delete_question function is inside a React Functional Component.

  • Related