Is it possible to dipatsch on useSelector function? - reactjs

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

Related

react.js useState doesn't update the

I have a series of 4 dropdowns with filtering of the available items of the 2nd based on the selection of the 1st, and so on down to the 4th. The 1st, 2nd and 3rd work as expected, but the 4th never has any items in it. All of the typeahead dropdowns use the same component, so there is no difference in the code used between them.
Here is the component that is giving me grief, the relevant typeahead is the last that uses availableSpots for its items.
import React, { useState, useEffect } from "react";
import Button from "react-bootstrap/Button";
import "bootstrap/dist/css/bootstrap.min.css";
import { Table } from "react-bootstrap";
import { UseFetchCountries, UseFetchStates, UseFetchWaters, UseFetchSpots } from "./hooks/SearchBarHooks";
import TypeAheadDropDown from "./Typeaheads/TypeAheadDropDown";
const SearchBar = () => {
const dbCountries = UseFetchCountries("http://localhost:3033/country");
const dbStates = UseFetchStates("http://localhost:3033/state");
const dbWaters = UseFetchWaters("http://localhost:3033/water");
const dbSpots = UseFetchSpots("http://localhost:3033/spot");
const [availableStates, setAvailableStates] = useState(dbStates);
const [availableWaters, setAvailableWaters] = useState(dbWaters);
const [availableSpots, setAvailableSpots] = useState(dbSpots);
const [selectedCountry, setSelectedCountry] = useState({});
const [selectedState, setSelectedState] = useState({});
const [selectedWater, setSelectedWater] = useState({});
const [selectedSpot, setSelectedSpot] = useState({});
const selectCountryHandler = (country) => {
setSelectedCountry(country);
setAvailableStates(dbStates.filter((state) => state.country_id == country.id));
console.log("===> selectCountryHandler > availableSpots: " + JSON.stringify(availableSpots));
};
const selectStateHandler = (state) => {
setSelectedState(state);
setAvailableWaters(dbWaters.filter((water) => state.id == water.state_id));
console.log("===> selectStateHandler > availableSpots: " + JSON.stringify(availableSpots));
};
const selectWaterHandler = (water) => {
setSelectedWater(water);
console.log("===> water: " + JSON.stringify(water));
console.log("===> dbSpots: " + JSON.stringify(dbSpots));
setAvailableSpots(dbSpots.filter((spot) => spot.water_id == water.id));
console.log("===> Filtered Spots: " + JSON.stringify(dbSpots.filter((spot) => spot.water_id == water.id)));
console.log("===> selectStateHandler > availableSpots: " + JSON.stringify(availableSpots));
};
const selectSpotHandler = (spot) => {
setSelectedSpot(spot);
};
const isButtonEnabled = () => {
if (selectedCountry.id && selectedState.id && selectedWater.id && selectedSpot.id) {
return "{{false}}";
}
return "{{disabled}}";
};
useEffect(() => {
setAvailableSpots(dbSpots.filter((spot) => spot.water_id == selectedWater.id));
}, [dbSpots, selectedWater]);
return (
<div className="container-fluid h-100">
<div className="row bordered-component">
<h4>Select your filters</h4>
<Table className="table search-bar-filter">
<tbody>
<tr>
<td className="dd-label-cell">
<h6>Country: </h6>
</td>
<td>
<TypeAheadDropDown items={dbCountries} countrySelected={selectCountryHandler} />
</td>
</tr>
<tr>
<td className="dd-label-cell">
<h6>State/Region: </h6>
</td>
<td>
<TypeAheadDropDown items={availableStates} parent={selectedCountry} stateSelected={selectStateHandler} />
</td>
</tr>
<tr>
<td className="dd-label-cell">
<h6>Water: </h6>
</td>
<td>
<TypeAheadDropDown items={availableWaters} parent={selectedState} waterSelected={selectWaterHandler} />
</td>
</tr>
<tr>
<td className="dd-label-cell">
<h6>Spot: </h6>
</td>
<td>
<TypeAheadDropDown items={availableSpots} parent={selectedWater} spotSelected={selectSpotHandler} />
</td>
</tr>
<tr>
<td> </td>
<td>
<Button className="float-right" variant="success" disabled={isButtonEnabled()}>
Get Fishing Reports
</Button>
</td>
</tr>
</tbody>
</Table>
</div>
</div>
);
};
export default SearchBar;
The console logs show that the data is retrieved from the REST endpoint in the useFetch* functions and dbSpots is properly set. But availableSpots is never set.

Display a table in React using different components

I'm just starting to learn React and what I'm trying to do is to display a table. I'm doing it using 3 components: App.js, DisplayTable.js, and TableRows.js. The reason I'm doing it this way is that later on, I will need certain rows of the table to be displayed subject to different conditions. I don't get any error messages, but the table is not being displayed. This is my code:
App.js:
import Form from './components/Form'
import { useState } from 'react'
import TableDisplay from './components/TableDisplay'
const App = () => {
const [rows, setRows] = useState([
{
id:1,
description:'',
semester:'',
prefix:'ENG',
number:'368/371',
grade:'',
editing:''
},
{
id:2,
description:'',
semester:'',
prefix:'',
number:'',
grade:'',
editing:''
},
{
id:3,
description:'',
semester:'',
prefix:'',
number:'',
grade:'',
editing:''
},
])
return (
<div className="container">
<Form/>
<TableDisplay rows={rows}/>
</div>
);
}
export default App;
TableDisplay.js:
import TableRows from "./TableRows"
const TableDisplay = ({rows}) => {
return (
<>
{rows.map((row) => {
<TableRows key={row.id} row={row}/>
})}
</>
)
}
export default TableDisplay
TableRows.js:
import React from 'react'
const TableRows = ({row}) => {
return (
<div className="container">
<table className="table table-striped">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Description</th>
<th scope="col">Semester</th>
<th scope="col">Prefix</th>
<th scope="col">Number</th>
<th scope="col">Grade</th>
<th scope="col">Editing</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">{row.id}</th>
<td>{row.description}</td>
<td>{row.semester}</td>
<td>ENG</td>
<td>368/371</td>
<td>{row.grade}</td>
<td><button type="button" className="btn btn-warning">Edit</button></td>
</tr>
<tr>
<th scope="row">{row.id}</th>
<td>{row.description}</td>
<td>{row.semester}</td>
<td>ENG</td>
<td>368/371</td>
<td>{row.grade}</td>
<td><button type="button" className="btn btn-warning">Edit</button></td>
</tr>
<tr>
<th scope="row">{row.id}</th>
<td>{row.description}</td>
<td>{row.semester}</td>
<td>ENG</td>
<td>368/371</td>
<td>{row.grade}</td>
<td><button type="button" className="btn btn-warning">Edit</button></td>
</tr>
</tbody>
</table>
</div>
)
}
export default TableRows
You should remove bracket
const TableDisplay = ({ rows }) => {
return (
<>
{rows.map((row) => <TableRows key={row.id} row={row} />)}
</>
);
};
or add return
const TableDisplay = ({ rows }) => {
return (
<>
{rows.map((row) => {
return <TableRows key={row.id} row={row} />;
})}
</>
);
};

How can i change this code to react redux

Here i want to change this code to react redux. How i can change this code using react redux. Kindly provide any solutions for changing this code to react redux using GET method api. As iam new to react js how can i change this code using react redux.
import React from "react";
import { useState, useEffect } from "react";
import { Link } from "react-router-dom";
export default function User() {
const [users, setUsers] = useState([]);
const f = async () => {
const res = await fetch("https://reqres.in/api/userspage=1");
const json = await res.json();
setUsers(json.data);
};
useEffect(() => {
f();
}, []);
const handleLogout = (e) => {
localStorage.clear();
window.location.pathname = "/";
}
return (
<div>
<h1>List Users</h1>
<div>
<button onClick={handleLogout}>Logout</button>
<nav>
<Link to="/Home">Home</Link>
</nav>
<table class="table">
<thead>
<tr>
<th>Id</th>
<th>First_name</th>
<th>Last_name</th>
<th>Email</th>
<th>Avatar</th>
</tr>
</thead>
<tbody>
{users.length &&
users.map((user) => {
return (
<tr>
<td> {user.id}</td>
<td>{user.first_name}</td>
<td> {user.last_name} </td>
<td>{user.email}</td>
<td> <img key={user.avatar} src={user.avatar} alt="avatar" /></td>
</tr>
);
})}
</tbody>
</table>
</div>
</div>
);
}

" Cannot read property 'map' of undefined"

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.

No access to "this"

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

Resources