简体   繁体   中英

Remove row from React table without refreshing page

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 deleteCriteriaById is not defined. Is this supposed to be my reducer function? How would I implement it so that it sends the new array of data? 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>
          {mapCriterias.map(item => {
            return (
              <tr key={item.id}>
                <td>{ item.id }</td>
                <td>{ item.criteria }</td>
                <td><DeleteButton deleteCriteria={dispatch(deleteCriteriaById)} /></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 }),
  }
})

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

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:

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

And then your DeleteButton component would look something like:

export const DeleteButton = ({deleteCriteria, id}) => {

    const delete_question = async () => {
        try {
            const response = await axios.delete(`http://localhost:3001/criteria/${encodeURIComponent(key)}`);

            deleteCriteria(id)
        } catch (error) {
            // handle errors
        }

    }

    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>
    </>
  )
    
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM