TypeError: Cannot read property 'map' of undefined
Education
import { deleteEducation } from '../../actions/profile';
const Education = ({ education, deleteEducation }) => {
const educations = education.map(edu => (
<tr key={edu._id}>
<td>{edu.school}</td>
<td className="hide-sm">{edu.degree}</td>
<td>
here is my code of education.js
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import Moment from 'react-moment';
import moment from 'moment';
import { connect } from 'react-redux';
import { deleteEducation } from '../../actions/profile';
const Education = ({ education, deleteEducation }) => {
const educations = education.map(edu => (
<tr key={edu._id}>
<td>{edu.school}</td>
<td className="hide-sm">{edu.degree}</td>
<td>
<Moment format="YYYY/MM/DD">{moment.utc(edu.from)}</Moment> -{' '}
{edu.to === null ? (
' Now'
) : (
<Moment format="YYYY/MM/DD">{moment.utc(edu.to)}</Moment>
)}
</td>
<td>
<button
onClick={() => deleteEducation(edu._id)}
className="btn btn-danger"
>
Delete
</button>
</td>
</tr>
));
return (
<Fragment>
<h2 className="my-2">Education Credentials</h2>
<table className="table">
<thead>
<tr>
<th>School</th>
<th className="hide-sm">Degree</th>
<th className="hide-sm">Years</th>
<th />
</tr>
</thead>
<tbody>{educations}</tbody>
</table>
</Fragment>
);
};
Education.propTypes = {
education: PropTypes.array.isRequired,
deleteEducation: PropTypes.func.isRequired
};
export default connect(
null,
{ deleteEducation }
)(Education);
try this
const educations = education && education.map(edu => .....
By using this if education field not exist and empty screen display instead of error.
Related
Langage used : JS with REACT REDUX
The context : I have a component who render a list of quotes following the user filter and categories choice.
In my filter component, i store the select value (buttonsData), and here i re render a certains component depending on select value.
import React from 'react';
import { Table } from 'react-bootstrap';
import { useSelector } from 'react-redux';
//here each component following the user choice
import { AllForms } from './categories/AllForms';
import { AtoZ } from './sorted/AtoZ';
import { ZtoA } from './sorted/ZtoA';
import { Ascend } from './sorted/Ascend';
import CurrentOffers from './categories/CurrentOffers';
import ValidateOffers from './categories/ValidateOffers';
export const OfferList = () => {
const buttonsData = useSelector((state) => state.buttonReducer);
return (
<Table hover responsive="md" className="folder__table">
<thead className="folder__content">
<tr className="folder__titles">
<th className="folder__title"> </th>
<th className="folder__title">Order REF</th>
<th
className="folder__title"
>
Entité
</th>
<th className="folder__title">Customer</th>
<th className="folder__title">Status</th>
<th className="folder__title">Date</th>
<th className="folder__title "> </th>
</tr>
</thead>
{buttonsData.activeComponent === 'AllForms' && <AllForms />}
{buttonsData.activeComponent === 'Ascend' && <Ascend />}
{buttonsData.activeComponent === 'validate' && <ValidateOffers />}
</Table>
);
};
I have used createSelector to filter and sort my datas (working fine).
import { useSelector } from 'react-redux';
export const SelectOffersValidate = () => {
//here i select ALL my forms, get with axios
const formsDatas = useSelector((state) => state.offersReducer);
const sortedForms = [...formsDatas].filter(
(oneOffer) => oneOffer.status == 'validate'
);
console.log(sortedForms);
return sortedForms;
};
export const SelectOffersAscend = () => {
const formsDatas = useSelector((state) => state.offersReducer);
const sortedForms = [...formsDatas].sort((a, b) =>
b.createdAt.localeCompare(a.createdAt)
);
return sortedForms;
};
Here a component filtered ( i have one component for AllForms, one for Validate and one for ascend, exaclty the same but with own select function)
import React, { useState } from 'react';
import { FiEdit3 } from 'react-icons/fi';
import {
SelectOffersAscend,
} from '../../../selector/projects.selector.js';
import { isEmpty } from '../../../middlewares/verification.js';
import Moment from 'react-moment';
export const Ascend = () => {
const formsAscend = SelectOffersAscend();
return (
<>
<tbody>
{!isEmpty(formsAscend[0]) &&
formsAscend?.map((oneForm) => {
return (
<tr key={oneForm.id}>
<td>
<input
type="checkbox"
/>
</td>
<td>{oneForm.ref} </td>
<td> {oneForm.entity}</td>
<td>{oneForm.customer} </td>
<td>{oneForm.status} </td>
<td>
<Moment format="DD/MM/YYYY" date={oneForm.createdAt} />
</td>
<td>
<FiEdit3 />
</td>
</tr>
);
})}
</tbody>
</>
);
};
My first problem :
I have made a component for EACH filter, but it's repetitive, is there a better way to do ?
The second problem :
"AllForms" and "ValidateOffers" are categories and "Ascend" is a filter.
For the moment i filter only with AllForms but i would like to filtered based on categories choosen.
I've tried to create an action to store the actual categories, so i've tried to dispatch on my createSelector validate function but it's looping so i don't think is the best way to do
SOLUTION : thanks to Chris whol helped me :)
So i have delete all my filtered component to just have one and create a custom hook
import React, { useMemo } from 'react';
import { Table } from 'react-bootstrap';
import { useSelector } from 'react-redux';
import { OfferRows } from './OfferRows';
export const useFilteredOffers = () => {
const buttonsData = useSelector((state) => state.buttonReducer);
const offersData = useSelector((state) => state.offersReducer);
return useMemo(() => {
switch (buttonsData.activeComponent) {
case 'Ascend': // fix casing
return offersData?.sort((a, b) =>
b.createdAt.localeCompare(a.createdAt)
);
case 'validate':
return offersData?.filter((oneOffer) => oneOffer.status === 'validate');
case 'not validate':
return offersData?.filter(
(oneOffer) => oneOffer.status === 'not validate'
);
case 'AtoZ':
return offersData?.sort((a, b) => a.customer.localeCompare(b.customer));
case 'ZtoA':
return offersData?.sort((a, b) => b.customer.localeCompare(a.customer));
default:
return offersData;
}
}, [buttonsData.activeComponent, offersData]);
};
export const OfferList = () => {
const filteredOffers = useFilteredOffers();
return (
<Table hover responsive="md" className="folder__table">
<thead className="folder__content">
<tr className="folder__titles">
<th className="folder__title"> </th>
<th className="folder__title">Order REF</th>
<th className="folder__title">Entité</th>
<th className="folder__title">Customer</th>
<th className="folder__title">Status</th>
<th className="folder__title">Date</th>
<th className="folder__title "> </th>
</tr>
</thead>
<OfferRows offers={filteredOffers} />
</Table>
);
};
Here the rows
import React from 'react';
import { FiEdit3 } from 'react-icons/fi';
import Moment from 'react-moment';
import { isEmpty } from '../../middlewares/verification.js';
export const OfferRows = ({ offers }) => {
return (
<>
<tbody>
{!isEmpty(offers[0]) &&
offers?.map((oneForm) => {
return (
<tr key={oneForm.id}>
<td>
<input type="checkbox" />
</td>
<td>{oneForm.ref} </td>
<td> {oneForm.entity}</td>
<td>{oneForm.customer} </td>
<td>{oneForm.status} </td>
<td>
<Moment format="DD/MM/YYYY" date={oneForm.createdAt} />
</td>
<td>
<FiEdit3 />
</td>
</tr>
);
})}
</tbody>
</>
);
};
I would create a single component for the rendering of the offer rows. The data can be filtered using a single hook that also selects the active filter. You can also pass this down as an argument.
Custom hooks MUST start with the use keyword. See the Rules of Hooks documentation for more information.
const useFilteredOffers = () => {
const activeFilter = useSelector((state) => state.buttonReducer);
const offers = useSelector((state) => state.offersReducer);
return useMemo(() => {
switch (activeFilter) {
case 'Ascend': // fix casing
return offers?.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
case 'validate':
return offers?.filter(oneOffer => oneOffer.status == 'validate');
default:
return offers;
}
}, [activeFilter, offers]);
}
export const OfferList = () => {
const filteredOffers = useFilteredOffers();
return (
<Table hover responsive="md" className="folder__table">
<thead className="folder__content">
<tr className="folder__titles">
<th className="folder__title"> </th>
<th className="folder__title">Order REF</th>
<th
className="folder__title"
>
Entité
</th>
<th className="folder__title">Customer</th>
<th className="folder__title">Status</th>
<th className="folder__title">Date</th>
<th className="folder__title "> </th>
</tr>
</thead>
<OfferRows offers={filteredOffers} />
</Table>
);
};
For completeness, here is the OfferRows component.
PS: You won't need to use the isEmpty validator because Array#map won't have any effect when the Array is empty.
export const OfferRows = (offers) => {
return (
<>
<tbody>
{offers?.map((oneForm) => {
return (
<tr key={oneForm.id}>
<td>
<input
type="checkbox"
/>
</td>
<td>{oneForm.ref} </td>
<td> {oneForm.entity}</td>
<td>{oneForm.customer} </td>
<td>{oneForm.status} </td>
<td>
<Moment format="DD/MM/YYYY" date={oneForm.createdAt} />
</td>
<td>
<FiEdit3 />
</td>
</tr>
);
})}
</tbody>
</>
);
};
I have a component that I am trying to access data from my redux store. I am able, through my dev tools, to see that the data is populating into the redux store. However when I try to console.log that data to see the data before I implement it into my component I am getting undefined.
Here is my react component.
import React, { Fragment, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Table } from 'react-bootstrap';
import Moment from 'react-moment';
import Button from '../components/Button';
import ActivitySummary from '../components/ActivitySummary';
import { projectsInfoDetails } from '../actions/projectInfoActions';
import { projectContacts } from '../actions/projectContactActions';
import SectionHeader from '../components/SectionHeader';
import Loader from '../components/Loader';
import Message from '../components/Message';
const ProjectScreen = ({ match }) => {
const dispatch = useDispatch();
const projectInfoDetails = useSelector(state => state.projectInfoDetails);
const { loading, error, projects } = projectInfoDetails;
const contactDetails = useSelector(state => state.contactDetails);
const { projectContactDetails } = contactDetails;
useEffect(() => {
dispatch(projectsInfoDetails(match.params.id));
dispatch(projectContacts(match.params.id));
}, [dispatch, match]);
console.log(projectContactDetails);
return (
<Fragment>
<div>
<SectionHeader sectionName='Project' />
<Button buttonName='Edit Project' />
</div>
{loading ? (
<Loader />
) : error ? (
<Message variant='danger'>{error}</Message>
) : (
<div style={{ backgroundColor: '#F8F8F8' }}>
<Table className='borderless'>
<tbody>
<tr>
<td>
<strong>Name: </strong>
{projects.name}
</td>
<td>
<strong>Status: </strong>
{projects.status}
</td>
</tr>
<tr>
<td>
<strong>State: </strong>
{projects.state}
</td>
<td>
<strong>County: </strong>
{projects.county}
</td>
</tr>
<tr>
<td>
<strong>Congressional District: </strong>
{projects.district}
</td>
<td>
<strong>Type: </strong>
{projects.type}
</td>
</tr>
<tr>
<td>
<strong>Funding Source: </strong>
<br />
{`${projects.fundingSource} ${projects.fundingSourceName}`}
</td>
<td>
<strong>Funding Source Goal: </strong>
<br />
{projects.fundingSourceGoal}
</td>
<td>
<strong>Start Date: </strong>
<br />
<Moment format='MM/DD/YYYY'>{projects.startDate}</Moment>
</td>
<td>
<strong>End Date: </strong>
<br />
{projects.endDate === null ? (
''
) : (
<Moment format='MM/DD/YYYY'>{projects.endDate}</Moment>
)}
</td>
<td>
<strong>Funding Percent: </strong>
<br />
{projects.fundingPercent}
</td>
</tr>
<tr>
<td>
<strong>Contact: </strong>
{projects.contact}
</td>
</tr>
<tr>
<td>
<strong>Start Date: </strong>
<Moment format='MM/DD/YYYY'>
{projects.projectStartDate}
</Moment>
</td>
<td>
<strong>End Date: </strong>
{projects.projectEndDate === null ? (
''
) : (
<Moment format='MM/DD/YYYY'>
{projects.projectEndDate}
</Moment>
)}
</td>
</tr>
<tr>
<td colSpan='5'>
<strong>Goals and Objectives: </strong>
{projects.goalsAndObjectives}
</td>
</tr>
<tr>
<td colSpan='5'>
<strong>Success Description: </strong>
{projects.successDescription}
</td>
</tr>
<tr>
<td>
<strong>Accountable Staff</strong>
{projects.accountableStaff &&
projects.accountableStaff.map(data => (
<tr key={data._id}>
{data.lastName}, {data.firstName}
</tr>
))}
</td>
</tr>
<tr>
<td>
<strong>Project ID: </strong>
{projects.projectId}
</td>
</tr>
</tbody>
</Table>
</div>
)}
<ActivitySummary />
</Fragment>
);
};
export default ProjectScreen;
Here is my reducer:
import {
PROJECT_CONTACT_REQUEST,
PROJECT_CONTACT_SUCCESS,
PROJECT_CONTACT_FAIL
} from '../constants/projectConstants';
export const projectContactReducer = (
state = { projectContact: [] },
action
) => {
switch (action.type) {
case PROJECT_CONTACT_REQUEST:
return { loading: true, projectContact: [] };
case PROJECT_CONTACT_SUCCESS:
return { loading: false, projectContact: action.payload };
case PROJECT_CONTACT_FAIL:
return { loading: false, error: action.payload };
default:
return state;
}
};
and finally my action call:
import axios from 'axios';
import {
PROJECT_CONTACT_REQUEST,
PROJECT_CONTACT_SUCCESS,
PROJECT_CONTACT_FAIL
} from '../constants/projectConstants';
export const projectContacts = id => async dispatch => {
try {
dispatch({ type: PROJECT_CONTACT_REQUEST });
const { data } = await axios.get(`/api/projects/${id}/projectscontact`);
dispatch({
type: PROJECT_CONTACT_SUCCESS,
payload: data
});
} catch (error) {
dispatch({
type: PROJECT_CONTACT_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message
});
}
};
Here is what the response from the server looks like in my dev tools:
my guess is that it has something to do with the data not populating before the console.log, but cannot seem to access the data.
Thanks
TL
my combine reducers file:
import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import { projectDetailsReducer } from './reducers/projectReducer';
import { pageReducer } from './reducers/pageReducer';
import { projectInfoReducer } from './reducers/projectInfoReducer';
import { fundingSourceReducer } from './reducers/fundingSourcesReducer';
import { fundSourceReducer } from './reducers/fundingSourceReducer';
import { contactsReducer } from './reducers/contactsReducer';
import { contactReducer } from './reducers/contactReducer';
import { projectContactReducer } from './reducers/projectContactReducer';
const reducer = combineReducers({
projectDetails: projectDetailsReducer,
projectInfoDetails: projectInfoReducer,
pageDetails: pageReducer,
fundingSourceDetails: fundingSourceReducer,
fundSourceDetails: fundSourceReducer,
contactsDetails: contactsReducer,
contactDetails: contactReducer,
projectContactDetails: projectContactReducer
});
const initialState = {};
const middleware = [thunk];
const store = createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
You get projectContactDetails wrong. There is projectContact not projectContactDetails in state of the projectContactReducer reducer so you should get that like:
const contactDetails = useSelector(state => state.projectContactDetails);
const { projectContact } = contactDetails;
How to integrate pagination code with hooks method data table with. im using react-js-pagination nmp package but there is no one explanation for implement with hook method program.
This my data table code:
import React, { useEffect, useState } from 'react';
import axios from 'axios'
import 'bootstrap/dist/css/bootstrap.min.css';
import {Link} from 'react-router-dom';
const ProTable = () => {
const [data, setData] = useState([]);
useEffect(() => {
loadData();
}, []);
const loadData = async() => {
axios.get('http://localhost:5000/api/clientgetdata')
.then(response => {
setData(response.data.map);
}
const delPro = (item,e) => {
var option = window.confirm(`Are you sure to delete ${e.clientName} OF ${item.projectName}`)
if(option){
const check = axios.delete(`http://localhost:5000/api/clientdelpro/${e.clientName}/${item.projectName}`).then(res => {
//console.log(clientname)
window.location.reload(false)
})
}
}
return (
<>
<div className="row addButton">
<div className="col-lg-1">
<Link
className="btn btn-outline-primary mr-2"
to={'/client/addpro'}
>New</Link>
</div>
<div className="col-lg-1">
{/* <button variant="primary" >Delete</button> */}
</div>
</div>
<div className="row hrtable">
<div className="col-lg-10 col-sm-6 col-md-6">
<div className="table-responsive tcenter" >
<table className="table table-bordered table-hover table-sm">
<thead className="thead-dark">
<tr>
<th scope="col"><input type="checkbox" /></th>
<th scope="col">Client Name</th>
<th scope="col">Project Name</th>
<th scope="col">Status</th>
<th>Action</th>
</tr>
</thead>
{ (data.length > 0) ? data.map( e => {
return (
<>
{e.project.map(item=> {
return (
<tbody>
<tr>
<th scope="row">
<input type="checkbox"/>
</th>
<td><ul>{e.clientName}</ul></td>
<td><ul>{item.projectName}</ul></td>
<td><ul>{item.proStatus}</ul></td>
<td>
<Link
className="btn btn-outline-primary mr-2"
to={`/project/edit/${e.clientName}/${item.projectName}`} >
Edit
</Link>
<button
className="btn btn-danger"
onClick={() => delPro(item,e)}>
Delete
</button>
</td>
</tr>
</tbody>
);
})}
</>
);
}) : <tr><td colSpan="5">No Records Found</td></tr> }
</table>
</div>
</div>
</div>
</>
);
}
export default ProTable;
This is Reaci-js-pagination code.
I am trying to follow this tutorial to create a pagination in my application https://www.npmjs.com/package/react-js-pagination#usage
import React, { Component } from "react";
import ReactDOM from "react-dom";
import Pagination from "react-js-pagination";
require("bootstrap/less/bootstrap.less");
class App extends Component {
constructor(props) {
super(props);
this.state = {
activePage: 15
};
}
handlePageChange(pageNumber) {
console.log(`active page is ${pageNumber}`);
this.setState({activePage: pageNumber});
}
render() {
return (
<div>
<Pagination
activePage={this.state.activePage}
itemsCountPerPage={10}
totalItemsCount={450}
pageRangeDisplayed={5}
onChange={this.handlePageChange.bind(this)}
/>
</div>
);
}
}
plz help me how integrate both code
Try by converting it into hooks
const [state, setState] = React.useState({activePage: 15});
const handlePageChange=(pageNumber) => {
setState({activePage: pageNumber});
// make api call for next page
}
return (
<div>
<Pagination
activePage={state.activePage}
itemsCountPerPage={10} // pass your fixed item per pages
totalItemsCount={450} // total item -> per-page * no of page
pageRangeDisplayed={5}
onChange={handlePageChange}
/>
</div>
);
I'm working on a web-application using the MERN stack that displays a table of clients with their name, email, and phone number. I haven't implemented Redux quite yet, but I'm using 'uuid' to supplement data in the table until I can get the redux store set up. So far I have displaying the the list and adding a client to the list working fine, but I am having trouble with the pesky delete button.
This is the current ClientTable component
import React, { Component } from "react";
import { Table, Container, Button } from "reactstrap";
import { connect } from "react-redux";
import {
getClients,
addClient,
editClient,
deleteClient,
} from "../actions/clientActions";
import PropTypes from "prop-types";
const renderClient = (clients, index, id) => {
return (
<tr key={index}>
<td>
<Button
className="remove-btn"
color="danger"
size="sm"
onClick={() => {
this.setState((state) => ({
clients: state.clients.filter((client) => client.id !== id),
}));
}}
>
×
</Button>
</td>
<td>{clients.name}</td>
<td>{clients.email}</td>
<td>{clients.number}</td>
</tr>
);
};
class ClientTable extends Component {
componentDidMount() {
this.props.getClients();
}
onDeleteClick = (id) => {
this.props.deleteClient(id);
};
render() {
const { clients } = this.props.client;
// const { clients } = this.state;
return (
<Container id="listContainer">
<Table
id="listTable"
className="table-striped table-bordered table-hover"
dark
>
<tr class="listRow">
<thead id="tableHeader">
<tr>
<th id="listActions">Actions</th>
<th id="listName">Name</th>
<th id="listEmail">Email</th>
<th id="listNumber">Number</th>
</tr>
</thead>
<tbody class="listRow">{clients.map(renderClient)}</tbody>
</tr>
</Table>
</Container>
);
}
}
ClientTable.propTypes = {
getClients: PropTypes.func.isRequired,
client: PropTypes.object.isRequired,
};
const mapStateToProps = (state) => ({
client: state.client,
});
export default connect(mapStateToProps, {
getClients,
deleteClient,
addClient,
})(ClientTable);
This is the bit of code that is causing me issues
<Button
className="remove-btn"
color="danger"
size="sm"
onClick={() => {
this.setState((state) => ({
clients: state.clients.filter((client) => client.id !== id),
}));
}}
>
×
</Button>
When I click the "delete" button I keep getting TypeError: Cannot read property 'setState' of unedefined
I know the error is because of 'this' isn't bound to anything, but I'm uncertain how to bind it within an onClick event if that is even possible or what even to bind it to. I am just lost as to how to approach this problem. (I'm still quite new to React).
If anyone has any ideas it would be greatly appreciated!
move renderClient function to ClientTable, and use it as a method of this class.
class ClientTable extends Component {
componentDidMount() {
this.props.getClients();
}
renderClient = (clients, index) => {
return (
<tr key={index}>
<td>
<Button
className="remove-btn"
color="danger"
size="sm"
onClick={() => this.onDeleteClient(clients.id)}
>
×
</Button>
</td>
<td>{clients.name}</td>
<td>{clients.email}</td>
<td>{clients.number}</td>
</tr>
);
};
onDeleteClick = (id) => {
this.props.deleteClient(id);
};
render() {
const { clients } = this.props.client;
// const { clients } = this.state;
return (
<Container id="listContainer">
<Table
id="listTable"
className="table-striped table-bordered table-hover"
dark
>
<tr class="listRow">
<thead id="tableHeader">
<tr>
<th id="listActions">Actions</th>
<th id="listName">Name</th>
<th id="listEmail">Email</th>
<th id="listNumber">Number</th>
</tr>
</thead>
<tbody class="listRow">{clients.map(this.renderClient)}</tbody>
</tr>
</Table>
</Container>
);
}
}
I follow a React/Redux tutorial and from what I saw on a few articles on internet I realized that inline functions are bad for performance in React.
From what I understood functions are reference type and if you use an inline function, for every re-render, this function will take a different spot in memory.
In the tutorial example I have this deleteExperience() method, that the instructor used inline.
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import Moment from 'react-moment';
import { Link, withRouter } from 'react-router-dom';
import { deleteExperience } from '../../actions/profileActions';
const Experience = ({ experience, deleteExperience }) => {
const experiences = experience.map(exp => (
<tr key={exp._id}>
<td>{exp.company}</td>
<td className="hide-sm">{exp.title}</td>
<td>
<Moment format="YYYY/MM/DD">{exp.from}</Moment> -
{exp.to === null ? (
' Now '
) : (
<Moment format="YYYY/MM/DD">{exp.to}</Moment>
)}
</td>
<td>
<button className="btn btn-danger" onClick={() => deleteExperience(exp._id)}>
Delete
</button>
</td>
</tr>
));
return (
<Fragment>
<h2 className="my-2">Experience Credentials</h2>
<table className="table">
<thead>
<tr>
<th>Company</th>
<th className="hide-sm">Title</th>
<th className="hide-sm">Years</th>
<th />
</tr>
</thead>
<tbody>{experiences}</tbody>
</table>
</Fragment>
);
};
Experience.propTypes = {
experience: PropTypes.array.isRequired,
deleteExperience: PropTypes.func
};
export default connect(
null,
{deleteExperience}
)(withRouter(Experience));
So the instructor said that he used inline function
onClick={() => deleteExperience(exp._id)}
and not just called directly the function
onClick={deleteExperience(exp._id)}
to not be execute immediately.
So, please someone tell me, if the theory about bad practice to use inline function is true, how to handle this situation? I tried many ways, without any success.
The performance issue isn't from using arrow functions, but rather from creating fresh ones on every render. In your case, you can use useCallback() to memoize them. (You'll need to extract a component to render each exp object to avoid breaking the rules of hooks.) Something like this should work:
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import Moment from 'react-moment';
import { Link, withRouter } from 'react-router-dom';
import { deleteExperience } from '../../actions/profileActions';
const Exp = ({ exp, deleteExperience }) => {
const del = useCallback(() => deleteExperience(exp._id), [deleteExperience, exp._id]);
return (
<tr>
<td>{exp.company}</td>
<td className="hide-sm">{exp.title}</td>
<td>
<Moment format="YYYY/MM/DD">{exp.from}</Moment> -
{exp.to === null ? (
' Now '
) : (
<Moment format="YYYY/MM/DD">{exp.to}</Moment>
)}
</td>
<td>
<button className="btn btn-danger" onClick={del}>
Delete
</button>
</td>
</tr>
);
};
const Experience = ({ experience, deleteExperience }) => {
const experiences = experience.map(exp => (
<Exp key={exp._id} exp={exp} deleteExperience={deleteExperience} />
));
return (
<Fragment>
<h2 className="my-2">Experience Credentials</h2>
<table className="table">
<thead>
<tr>
<th>Company</th>
<th className="hide-sm">Title</th>
<th className="hide-sm">Years</th>
<th />
</tr>
</thead>
<tbody>{experiences}</tbody>
</table>
</Fragment>
);
};
Experience.propTypes = {
experience: PropTypes.array.isRequired,
deleteExperience: PropTypes.func
};
export default connect(
null,
{deleteExperience}
)(withRouter(Experience));