Mike Miller
Mike Miller

Reputation: 3129

How to deselect list items in react admin custom bulk action buttons

I have a react admin project and have implemented a custom button for the List view passed into the bulkActionButtons attribute as described by the docs here: https://marmelab.com/react-admin/List.html#bulk-action-buttons

const BulkUserActions = props => (
  <MakeAdminButton {...props}/>
);

const UserList = props => (
  <List {...props} bulkActionButtons={<BulkUserActions/>}>
    <Datagrid rowClick="show">
      <TextField source="id"/>
      <EmailField source="email"/>
    </Datagrid>
  </List>
);

The MakeAdminButton handles the click and performs the update that I need. The missing piece of the puzzle is how to deselect the list items after the action is completed. The props.selectedIds is protected so I am unable to simply set this to an empty array after completing my logic.

Question is how to unset props.selectedIds or other method for deselecting the list items on completion.

const MakeAdminButton = withStyles(styles)(class MakeAdminButton extends React.Component {

  handleAction = () => {
    //does the stuff as required using this.props.selectedIds

    //what to return to unset this.props.selectedIds 
  };

  render () {
    return <Button variant="contained"
               color="primary"
               onClick={this.handleAction}
    <AdminIcon/>
  </Button>;
  }
});

Upvotes: 2

Views: 3796

Answers (3)

gstvg
gstvg

Reputation: 927

React Admin provide ways of doing it. On v3 directly with useUnselectAll or on onSuccess callback of useMutation, useUpdateMany and useDataProvider hooks. On v2 with dataProvider fourth argument of withDataProvider decorator or seting meta.onSuccess.unselectAll to true of custom redux actions, or automatically with crudUpdateMany, or options prop of Mutation component.

For v2 with crudUpdateMany, goto Mike Miller answer.

Working examples:

https://codesandbox.io/s/exciting-firefly-dd1ni?file=/src/App.js

v3:

const UnselectAllButtonV3Hook = props => {
  const unselectAll = useUnselectAll();

  return (
    <Button
      onClick={() => {
        unselectAll(props.resource);
      }}
    >
      Unselect all with v3 hook
    </Button>
  );
};

https://marmelab.com/react-admin/doc/3.5/Actions.html#handling-side-effects-in-usedataprovider

useUpdateMany/useDataProvider/useMutation hooks, onSuccess callback calling unselectAll() from useUnselectAll()

const MakeAdminButtonV3 = props => {
  const refresh = useRefresh();
  const unselectAll = useUnselectAll();

  const [makeAdmin, { loading }] = useUpdateMany(
    props.resource,
    props.selectedIds,
    { isAdmin: true },
    {
      onSuccess: () => {
        unselectAll(props.resource);
        refresh();
      }
    }
  );

  //OR

  const dataProvider = useDataProvider();

  const makeAdmin2 = () => {
    dataProvider.updateMany(
      props.resource,
      { ids: props.selectedIds, data: { isAdmin: true } },
      {
        onSuccess: () => {
          unselectAll(props.resource);
          refresh();
        }
      }
    );
  };

  //OR

  const [makeAdmin3, { loading3 }] = useMutation(
    {
      type: "updateMany",
      resource: props.resource,
      payload: { ids: props.selectedIds, data: { isAdmin: true } }
    },
    {
      onSuccess: () => {
        unselectAll(props.resource);
        refresh();
      }
    }
  );

  return (
    <Button onClick={makeAdmin3} disabled={loading3}>
      Make Admin with V3 dataProvider hooks
    </Button>
  );
};

https://marmelab.com/react-admin/doc/3.5/Actions.html#usemutation-hook

https://marmelab.com/react-admin/doc/3.5/Actions.html#usedataprovider-hook

https://marmelab.com/react-admin/doc/3.5/Actions.html#specialized-hooks

V2 examples also work on v3, but Mutation and withDataProvider is legacy, with a small change while using dataProvider directly, since dataProvider api on v3 changed.

https://marmelab.com/react-admin/doc/3.5/Actions.html#legacy-components-query-mutation-and-withdataprovider

v2:

withDataProvider:

const UnmakeAdminButtonWithWithDataProvider = withDataProvider(
  class extends React.Component {
    handleClick = () => {
      this.props.dataProvider(
        UPDATE_MANY,
        "users",
        { ids: this.props.selectedIds, data: { isAdmin: false } },
        { onSuccess: { unselectAll: true, refresh: true } }
      );
    };

    render() {
      return (
        <Button onClick={this.handleClick}>
          Unmake Admin with withDataProvider
        </Button>
      );
    }
  }
);

https://marmelab.com/react-admin/doc/2.9/Actions.html#handling-side-effects

custom redux action:

import { connect } from 'react-redux';

const MAKE_ADMIN = "MAKE_ADMIN";

const makeAdmin = ids => ({
  type: MAKE_ADMIN,
  payload: { ids, data: { isAdmin: true } },
  meta: {
    fetch: UPDATE_MANY,
    resource: "users",
    onSuccess: { unselectAll: true, refresh: true }
  }
});

const MakeAdminButtonWithCustomAction = connect(
  null,
  { makeAdmin }
)(
  class extends React.Component {
    handleClick = () => {
      this.props.makeAdmin(this.props.selectedIds);
    };

    render() {
      return (
        <Button onClick={this.handleClick}>
          Make Admin With Custom Redux Action
        </Button>
      );
    }
  }
);

https://marmelab.com/react-admin/doc/2.9/Actions.html#adding-side-effects-to-actions

Mutation component:

const MakeAdminMutationV2 = props => (
  <Mutation
    type="updateMany" //{UPDATE_MANY}
    resource={props.resource}
    payload={{ ids: props.selectedIds, data: { isAdmin: true } }}
    options={{
      onSuccess: {
        unselectAll: true,
        refresh: true
      }
    }}
  >
    {approve => <Button onClick={approve}>Make admin with Mutation</Button>}
  </Mutation>
);

https://marmelab.com/react-admin/doc/2.9/Actions.html#query-and-mutation-components

Upvotes: 3

Mike Miller
Mike Miller

Reputation: 3129

The solution is to call a bulk helper function from react admin:

class MakeAdminButton extends React.Component {

handleAction = () => {
 const { basePath, crudUpdateMany, resource, selectedIds } = this.props;

 //custom code goes in here...

 //calling this function triggers the unset and closes the toolbar
 crudUpdateMany(resource, selectedIds, { isAdmin: true }, basePath);
 };

render () {
  return <Button label="Make Admin" onClick={this.handleAction}>
    <AdminIcon/>
  </Button>;
 }
};

const BulkUserActions = connect(undefined, { crudUpdateMany })(MakeAdminButton);

const UserList = props => (
 <List {...props} bulkActionButtons={<BulkUserActions/>}>
  <Datagrid>
   <TextField source="id"/>
   <EmailField source="email"/>
   <BooleanField source="isAdmin" label="Admin" />
 </Datagrid>
</List>
);

An example is shown on this page if I had bothered to read it properly: https://github.com/marmelab/react-admin/blob/master/docs/List.md#bulk-action-buttons

Upvotes: -1

joe hoeller
joe hoeller

Reputation: 1277

Set up some meta data for each time you click the checkbox, so it injects true or false into the array like:

listData = {
   ...
   isChecked : true
}

Then inside the function that handles the bulk operations, add a [listData].filter( ), (lodash filter is faster), to check and see which isChecked === true, and then set them to false, and update the value={bool} attribute on the checkbox input tag.

Upvotes: 0

Related Questions