Search code examples
reactjsreact-nativereduxcomponents

react-redux onClick function data migration problem


When the Click event is made on the button in the form section, I want to map the data in the initial state and move it to the ListGroupItem in the table part, but I could not succeed. I don't know if it is related to this situation, but Uncaught TypeError: dispatch is not a function error.
FormSection.js

```
import React, { Component } from "react";
import { connect } from "react-redux";
import { Field, FormSection, reduxForm } from "redux-form";
import { withTranslation } from "react-i18next";
import { bindActionCreators } from "redux";
import { Button, ButtonToolbar } from "reactstrap";
import alertify from "alertifyjs";
import "alertifyjs/build/css/alertify.css";
import InputFormSection from "../../../../shared/components/generic-form/FormSections/InputSections/InputFormSection";
import renderSelectField from "../../../../shared/components/form/Select";
import { rules } from "../../../../shared/components/generic-form/validate";
import * as addHelpActions from '../actions/addHelpActions'

class HelpdeskReduxForm extends Component {
  
  state = {
    data: [
      { id: 1, issueName: "Ürün Kontrolu" },
      { id: 2, issueName: "Ürün Yerleştirme" },
      { id: 3, issueName: "Üretim Bandı Konrolu" },
      { id: 4, issueName: "Üretim Bandı " },
      { id: 5, issueName: "Üretim" },
      { id: 6, issueName: "Kurulum" },
      { id: 7, issueName: "Teknik" },
      { id: 8, issueName: "s" },
      { id: 9, issueName: "d" },
      { id: 10, issueName: "a" },
    ],
    userName: "",
  };
  
  onChangeHandler = (event) => {
    // this.setState({ userName: event.target.value });

    let name = event.target.name;
    let value = event.target.value;

    this.setState({ [name]: value });
    console.log(event);
  };
  
  onSubmitHandler = (event) => {
    alertify.success("Talebiniz Başarıyla İletilmiştir.");
    event.preventDefault();
    
  };
  
  addToHelp = (help) => {
    this.props.actions.addToHelp({quantity:1 , help})
    alertify.success(help.issueName + "Talebiniz Eklendi")
    console.log(help);
    help.preventDefault();
  }
  
  render() {
    return (
      <form className="form form--horizontal" onSubmit={this.onSubmitHandler}>
        <FormSection>
          <InputFormSection
            onChange={this.addToHelp}
            name="firstname"
            label="İsim"
            component="input"
            props={{
              type: "text",
              placeholder: "İsim",
            }}
          />
        </FormSection>
        <FormSection name="">
          <InputFormSection
            name="lastName"
            label="Soyisim"
            validate={["required_array"]}
            props={{
              type: "text",
              placeholder: "Soyisim",
            }}
          />
        </FormSection>
        <FormSection name="" validate="required">
          <InputFormSection
            name="email"
            label="Email"
            validate={["required"]}
            props={{
              type: "text",
              placeholder: "Email",
            }}
          />
        </FormSection>

        <div className="form__form-group">
          <div className="form__form-group-field">
            <Field
              name="name"
              component={renderSelectField}
              options={
                this.state.data &&
                this.state.data.map((helpTopic) => {
                  return {
                    value: helpTopic.id,
                    label: helpTopic.issueName,
                  };
                })
              }
              validate={rules["required"]} //Controller
            />
          </div>
        </div>
        <div>
          <ButtonToolbar className="form__button-toolbar">
            <Button
            onClick={() => this.addToHelp(this.help)}
              color="primary"
              size="lg"
              type="submit"
              disabled={this.props.pristine || this.props.submitting}
            >
              Gönder 
            </Button>
          </ButtonToolbar>
        </div>
      </form>
    );
  }
}
function mapStateToProps(state){
  return {
    box: state.jobSaveReducer,
  }
}
function mapDispatchToProps(dispatch) {
  return {
    actions: {
      addToHelp: bindActionCreators(addHelpActions.addToHelp, dispatch)
    },
  };
}


HelpdeskReduxForm = reduxForm({
  form: "New_Helpdesk_Form", // a unique identifier for this form
  enableReinitialize: true,
})(HelpdeskReduxForm);

export default connect(
  
  mapDispatchToProps, mapStateToProps
)(withTranslation("common")(HelpdeskReduxForm));


```

**saveReducer.js**
```
import * as actionTypes from '../actions/actionTypes'
import initialState from './initialState'

export default function jobSaveReducer(state = initialState.box, action){
    switch (action.type) {
        case actionTypes.ADD_TO_HELP:
            var addedHelp = state.find(c => c.help.id === action.payload.help.id);
            if(addedHelp){
                var newState = state.map(helpTopic => {
                    if(helpTopic.help.id === action.helpTopic.help.id){
                        return Object.assign({},addedHelp,{quantity: addedHelp.quantity + 1})
                    }
                    return helpTopic;
                })
                return newState;
            }else{
                return [...state, {...action.payload}] // copy the state and add action payload ( redux ile push, pop yapılmaz)
            }
        default:
            return state;
    }
}

**TablePart.js**

```
import React, { PureComponent } from "react";
import PropTypes from "prop-types";
import {
  Button,
  ButtonGroup,
  UncontrolledTooltip,
  Badge,
  ListGroup,
  ListGroupItem,
  Label,
} from "reactstrap";
import { withTranslation } from "react-i18next";
import { withRouter } from "react-router-dom";
import MaterialTable from "material-table";

// Material Theme
import { MuiThemeProvider, createMuiTheme } from "@material-ui/core/styles";

//Material Icons
import Search from "@material-ui/icons/Search";
import ViewColumn from "@material-ui/icons/ViewColumn";
import SaveAlt from "@material-ui/icons/SaveAlt";
import ArrowUpward from "@material-ui/icons/ArrowUpward";
import ChevronLeft from "@material-ui/icons/ChevronLeft";
import ChevronRight from "@material-ui/icons/ChevronRight";
import FirstPage from "@material-ui/icons/FirstPage";
import LastPage from "@material-ui/icons/LastPage";
import Check from "@material-ui/icons/Check";
import FilterList from "@material-ui/icons/FilterList";
import Remove from "@material-ui/icons/Remove";
import DeleteOutline from "@material-ui/icons/DeleteOutline";
import Clear from "@material-ui/icons/Clear";
import { connect } from "react-redux";
import jobSaveReducer from "../../NewTicket/reducers/jobSaveReducer";

class Table extends PureComponent {
  static propTypes = {
    t: PropTypes.func.isRequired,
    columns: PropTypes.array,
    data: PropTypes.any,
    actions: PropTypes.array,
    isRemoteData: PropTypes.bool,
    changePageorPageSize: PropTypes.func,
    toolbar: PropTypes.bool,
    paging: PropTypes.bool,
    search: PropTypes.bool,
    onRowClick: PropTypes.func,
    onRowClickSelection: PropTypes.bool,
    selection: PropTypes.bool,
    selectionOnlyOneRow: PropTypes.bool,
    onSelectionChange: PropTypes.func,
    showSelectAllCheckbox: PropTypes.bool,
    minBodyHeight: PropTypes.string,
    maxBodyHeight: PropTypes.string,
  };

  static defaultProps = {
    columns: [],
    data: [],
    actions: [],
    isRemoteData: false,
    toolbar: true,
    paging: true,
    search: true,
    onRowClick: undefined,
    onRowClickSelection: false,
    selection: false,
    selectionOnlyOneRow: false,
    onSelectionChange: undefined,
    showSelectAllCheckbox: false,
    minBodyHeight: "auto",
    maxBodyHeight: "auto",
  };

  constructor(props) {
    super(props);
    this.tableRef = React.createRef();
    this.theme = createMuiTheme({
      palette: {
        secondary: {
          main: "#70bbfd",
        },
      },
      typography: {
        fontFamily: "Source Sans Pro, sans-serif",
        fontSize: 14,
        color: "#4D4F5C !important",
      },
      overrides: {
        MuiTableHead: {
          root: {},
        },
        MuiTableCell: {
          head: {
            fontFamily: "Source Sans Pro, sans-serif",
            fontSize: "14px",
            backgroundColor: "#fafbfe !important",
            fontWeight: 600,
            padding: "9px 4px 9px 4px",
            color: "#4D4F5C !important",
            textAlign: "left !important",
          },
          root: {
            fontFamily: "Source Sans Pro, sans-serif",
            padding: "2px 4px 4px 4px",
            borderBottom: "1px solid #fff",
            color: "#4D4F5C !important",
            minHeight: "40px !important",
            height: "40px !important",
            verticalAlign: "middle",
          },
          footer: {
            fontFamily: "Source Sans Pro, sans-serif",
            minHeight: "auto !important",
            height: "auto !important",
          },
        },
        MuiTableRow: {
          root: {
            "&:nth-child(odd)": {
              backgroundColor: "#f4f6fd",
            },
            "&:nth-child(even)": {
              backgroundColor: "#fafbfe",
            },
            "&:hover": {
              backgroundColor: "#e9edf7 !important",
            },
          },
          footer: {
            backgroundColor: "#fff !important",
          },
        },
        MuiTablePagination: {
          root: {
            "&:hover": {
              backgroundColor: "#fff !important",
            },
          },
        },
      },
    });
  }

  componentWillReceiveProps(newProps) {
    const oldProps = this.props;
    const { isRemoteData } = this.props;

    if (
      isRemoteData &&
      oldProps.data !== newProps.data &&
      newProps.data.length !== 0
    ) {
      this.tableRef.current &&
        this.tableRef.current.onQueryChange({
          page: newProps.data.page,
          pageSize: newProps.data.pageSize,
        });
    }
  }

  onRowClick = (event, rowData) => {
    const { onRowClick, onRowClickSelection } = this.props;
    if (onRowClick) {
      if (onRowClickSelection) {
        this.addOneRowChecked(rowData.tableData.id);
      }
      onRowClick(rowData);
    }
  };

  removeFromChecked = (idx) => {
    if (!this.tableRef) {
      return;
    }
    this.tableRef.current.dataManager.changeRowSelected(false, [idx]);
  };

  addOneRowChecked = (idx) => {
    if (!this.tableRef) {
      return;
    }
    this.tableRef.current.onAllSelected(false);
    this.tableRef.current.dataManager.changeRowSelected(true, [idx]);
  };

  onSelectionChange = (event) => {
    const { selectionOnlyOneRow, onSelectionChange } = this.props;
    if (event.length >= 1 && selectionOnlyOneRow) {
      this.removeFromChecked(event[0].tableData.id);
    }
    if (onSelectionChange) {
      onSelectionChange(event);
    }
  };
  renderSummary() {
    return (
      <ListGroup>
        {this.props.box.map((helpTopic) => (
          <ListGroupItem key={helpTopic.help.id}> {helpTopic.help.issueName} </ListGroupItem>
        ))}
      </ListGroup>
    );
  }
  renderEmpy() {
    return(
      <Label>Kayıt Bulunamadı</Label>
    );
  }

  render() {
    const {
      t,
      i18n,
      columns,
      data,
      actions,
      isRemoteData,
      changePageorPageSize,
      toolbar,
      paging,
      search,
      onRowClick,
      selection,
      showSelectAllCheckbox,
      minBodyHeight,
      maxBodyHeight,
    } = this.props;

    let columnss = [];
    if (columns !== undefined) {
      columnss = columns.map((key) => {
        if (key.field === "checkpoint_form_name") {
          return {
            title: key.title,
            field: key.field,
            render: (rowData) => {
              if (rowData["form_name"] && Array.isArray(rowData["form_name"])) {
                var str = "";
                rowData["form_name"].forEach(function (obj, index) {
                  if (index !== rowData["form_name"].length - 1) {
                    str += obj.toString() + ", ";
                  } else {
                    str += obj.toString();
                  }
                });
                return str;
              }
            },
          };
        } else if (key.field === "priority_trName") {
          return {
            title: key.title,
            field: key.field,
            render: (rowData) => {
              switch (rowData.priority_trName) {
                case "Acil":
                  return (
                    <p style={{ color: "#e1001f" }}>
                      {rowData.priority_trName}
                    </p>
                  );
                case "Yüksek":
                  return (
                    <p style={{ color: "#ff8348" }}>
                      {rowData.priority_trName}
                    </p>
                  );
                case "Orta":
                  return (
                    <p style={{ color: "#f6da6e" }}>
                      {rowData.priority_trName}
                    </p>
                  );
                case "Düşük":
                  return (
                    <p style={{ color: "#70bbfd" }}>
                      {rowData.priority_trName}
                    </p>
                  );
              }
            },
          };
        } else if (key.field === "priority_enName") {
          return {
            title: key.title,
            field: key.field,
            render: (rowData) => {
              switch (rowData.priority_enName) {
                case "Urgent":
                  return (
                    <p style={{ color: "#e1001f" }}>
                      {rowData.priority_enName}
                    </p>
                  );
                case "High":
                  return (
                    <p style={{ color: "#ff8348" }}>
                      {rowData.priority_enName}
                    </p>
                  );
                case "Medium":
                  return (
                    <p style={{ color: "#f6da6e" }}>
                      {rowData.priority_enName}
                    </p>
                  );
                case "Low":
                  return (
                    <p style={{ color: "#70bbfd" }}>
                      {rowData.priority_enName}
                    </p>
                  );
              }
            },
          };
        } else if (key.field === "status_enName") {
          return {
            title: key.title,
            field: key.field,
            render: (rowData) => {
              switch (rowData.status_enName) {
                case "Waiting":
                  return (
                    <Badge style={{ backgroundColor: "#f6da6e" }}>
                      {rowData.status_enName}
                    </Badge>
                  );
                case "In Progress":
                  return (
                    <Badge style={{ backgroundColor: "#87c3f7" }}>
                      {rowData.status_enName}
                    </Badge>
                  );
                case "Delayed":
                  return (
                    <Badge style={{ backgroundColor: "#ff8348" }}>
                      {rowData.status_enName}
                    </Badge>
                  );
                case "Incomplete":
                  return (
                    <Badge style={{ backgroundColor: "#ff9d48" }}>
                      {rowData.status_enName}
                    </Badge>
                  );
                case "Not Done":
                  return (
                    <Badge style={{ backgroundColor: "#e1001f" }}>
                      {rowData.status_enName}
                    </Badge>
                  );
                case "Completed":
                  return (
                    <Badge style={{ backgroundColor: "#4ce1b6" }}>
                      {rowData.status_enName}
                    </Badge>
                  );
              }
            },
          };
        } else if (key.field === "status_trName") {
          return {
            title: key.title,
            field: key.field,
            render: (rowData) => {
              switch (rowData.status_trName) {
                case "Bekleyen":
                  return (
                    <Badge style={{ backgroundColor: "#f6da6e" }}>
                      {rowData.status_trName}
                    </Badge>
                  );
                case "Devam Ediyor":
                  return (
                    <Badge style={{ backgroundColor: "#87c3f7" }}>
                      {rowData.status_trName}
                    </Badge>
                  );
                case "Gecikmeli":
                  return (
                    <Badge style={{ backgroundColor: "#ff8348" }}>
                      {rowData.status_trName}
                    </Badge>
                  );
                case "Eksik Görev":
                  return (
                    <Badge style={{ backgroundColor: "#ff9d48" }}>
                      {rowData.status_trName}
                    </Badge>
                  );
                case "Yapılmadı":
                  return (
                    <Badge style={{ backgroundColor: "#e1001f" }}>
                      {rowData.status_trName}
                    </Badge>
                  );
                case "Tamamlandı":
                  return (
                    <Badge style={{ backgroundColor: "#4ce1b6" }}>
                      {rowData.status_trName}
                    </Badge>
                  );
              }
            },
          };
        } else if (key.field === "tour_id") {
          return {
            title: key.title,
            field: key.field,
            searchable: key.searchable,
            render: (rowData) => {
              return i18n.language === "tr"
                ? "Tur " + rowData.tour_id
                : "Tour " + rowData.tour_id;
            },
          };
        } else if (key.field === "event_description") {
          return {
            title: key.title,
            field: key.field,
            searchable: key.searchable,
            render: (rowData) => {
              if (
                rowData.event_type &&
                rowData.event_type === 6 &&
                rowData.form_name
              ) {
                return i18n.language === "tr"
                  ? rowData.form_name + " Cevaplandı "
                  : "Tour " + rowData.form_name + " Answered";
              } else {
                return i18n.language === "tr"
                  ? rowData.description_trName
                  : rowData.description_enName;
              }
            },
          };
        } else {
          return {
            title: key.title,
            field: key.field,
            searchable: key.searchable,
          };
        }
      });
    }
    return (
      <div className="material-table__wrap" style={{ width: "100%" }}>
        <MuiThemeProvider theme={this.theme}>
          <MaterialTable
            title="Styling with MuiThemeProvider Preview"
            columns={columnss}
            isLoading={data.loading}
            localization={{
              header: {
                actions: t("material_table_localization.header.actions"),
              },
              toolbar: {
                searchTooltip: t(
                  "material_table_localization.toolbar.searchTooltip"
                ),
                searchPlaceholder: t(
                  "material_table_localization.toolbar.searchTooltip"
                ),
              },

              body: {
                emptyDataSourceMessage:
                  this.props.box.length > 0
                    ? this.renderSummary()
                    : this.renderEmpy(), 
              },
              pagination: {
                labelRowsSelect: t(
                  "material_table_localization.pagination.labelRowsSelect"
                ),
                nextTooltip: t(
                  "material_table_localization.pagination.nextTooltip"
                ),
                previousTooltip: t(
                  "material_table_localization.pagination.previousTooltip"
                ),
                firstTooltip: t(
                  "material_table_localization.pagination.firstTooltip"
                ),
                lastTooltip: t(
                  "material_table_localization.pagination.lastTooltip"
                ),
                labelDisplayedRows: t(
                  "material_table_localization.pagination.labelDisplayedRows"
                ),
              },
            }}
            tableRef={this.tableRef}
            icons={{
              Check: () => <Check />,
              Export: () => <SaveAlt />,
              Filter: () => <FilterList />,
              FirstPage: () => <FirstPage />,
              LastPage: () => <LastPage />,
              NextPage: () => <ChevronRight />,
              PreviousPage: () => <ChevronLeft />,
              Search: () => <Search />,
              ResetSearch: () => <Clear />,
              ThirdStateCheck: () => <Remove />,
              ViewColumn: () => <ViewColumn />,
              DetailPanel: () => <ChevronRight />,
              Delete: () => <DeleteOutline />,
              SortArrow: (props) => <ArrowUpward {...props} fontSize="small" />,
            }}
            data={
              isRemoteData
                ? (query) =>
                    new Promise((resolve, reject) => {
                      resolve({
                        data: data && data.data ? data.data : [],
                        page: data.page,
                        totalCount: data.totalCount,
                      });
                    })
                : data || []
            }
            onChangePage={(page, pageSize) => {
              changePageorPageSize &&
                changePageorPageSize(
                  parseInt(page, 10),
                  parseInt(pageSize, 10)
                );
            }}
            actions={actions}
            components={{
              Actions: (props) => (
                <ButtonGroup
                  className="btn-group--icons table-actions-btn-group"
                  style={{ display: "block", marginBottom: 0 }}
                >
                  {props.actions.map((action, index) => (
                    <props.components.Action
                      action={action}
                      key={"action-" + index}
                      data={props.data}
                      size={props.size}
                      disabled={action.disabled && action.disabled(props.data)}
                      hidden={action.hidden && action.hidden(props.data)}
                    />
                  ))}
                </ButtonGroup>
              ),
              Action: (props) => (
                <>
                  <Button
                    id={`${props.action.name}_${props.data.tableData.id}`}
                    outline
                    style={{ marginBottom: 0 }}
                    disabled={props.disabled}
                    onClick={(event) => props.action.onClick(event, props.data)}
                  >
                    <i className={props.action.icon} />
                  </Button>
                  <UncontrolledTooltip
                    delay={{ show: 100, hide: 100 }}
                    placement="bottom"
                    target={`${props.action.name}_${props.data.tableData.id}`}
                  >
                    {props.action.tooltip}
                  </UncontrolledTooltip>
                </>
              ),
            }}
            options={{
              search: false,
              selection: selection,
              showSelectAllCheckbox: showSelectAllCheckbox,
              sorting: true,
              toolbar: toolbar,
              showTitle: false,
              paging: paging,
              emptyRowsWhenPaging: false,
              showFirstLastPageButtons: true,
              pageSizeOptions: [10, 20, 50],
              pageSize: data.pageSize || 20,
              minBodyHeight: minBodyHeight,
              maxBodyHeight: maxBodyHeight,
              detailPanelType: "single",
              rowStyle: (rowData, index) => ({
                background: rowData.tableData.checked && "#e9edf7",
              }),
            }}
            onSelectionChange={this.onSelectionChange}
            onRowClick={onRowClick ? this.onRowClick : undefined}
          />
        </MuiThemeProvider>
      </div>
    );
  }
}
function mapStateToProps(state) {
  return {
    box: state.jobSaveReducer,
  };
}

export default connect(mapStateToProps)(
  withRouter(withTranslation("common")(Table))
);
```

action to add function ActionPart

```
import * as actionTypes from "./actionTypes";

export function addToHelp(helpTopic) {
  return {
    type: actionTypes.ADD_TO_HELP,
    payload: helpTopic,
  };
}




```

Solution

  • You forgot to add mapDispatchToProps

    export default connect(mapStateToProps)( <-- HERE 
       withRouter(withTranslation("common")(Table))
    );
    

    Your code should be

    export default connect(mapDispatchToProps, mapStateToProps)(
       withRouter(withTranslation("common")(Table))
    );
    

    or you can replace mapDispatchToProps with the useDispatch Hook. The basic API looks like this:

    const dispatch = useDispatch();