react.js useState doesn't update the - reactjs

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.

Related

Deleting necessary data (with checkbox) in React

Need to delete the data that is highlighted by the checkbox. When I click on the checkbox, in all checkboxes the done becomes: true, then false and i can't remove the highlights. When the remove function is worked, only the first element is deleted. How can write a remove function.
import React from "react";
import { useState } from "react";
const App = () => {
const [user, setUser] = useState([
{id:1, name:"Peter", surname:"Robinson"},
{id:2, name:"Ann", surname:"Walker"},
{id:3, name:"James", surname:"Allen"},
])
const [check, setCheck] = useState({done: false})
const remove = () => {
if (check.done) {
}
}
return <>
<table className="table table-bordered">
<thead>
<tr>
{Object.keys(user[0]).map((elm,i) => {
return <td key={i}>
{elm.charAt(0).toUpperCase() + elm.slice(1)}
</td>
})}
</tr>
</thead>
<tbody>
{
user.map((elem, ind) => {
return <tr key={ind}>
<td>{elem.id}</td>
<td>{elem.name}</td>
<td>{elem.surname}</td>
<td>
<input type="checkbox" name="" id="" onChange={() => setCheck({done: check.done ? false : true})}/>
</td>
</tr>
})
}
</tbody>
</table>
<button className="btn btn-primary ms-2" onClick={() => remove()}>Delete selected</button>
</>
}
export default App;
Thank you.
You should handle the checked state for each user independently, then delete the ones with the checked flag at true:
import React from 'react';
import { useState } from 'react';
const App = () => {
const [user, setUser] = useState([
{ id: 1, name: "Peter", surname: "Robinson", checked: false },
{ id: 2, name: "Ann", surname: "Walker", checked: false },
{ id: 3, name: "James", surname: "Allen", checked: false }
]);
const toggleCheck = (id) => {
const checkedIdx = user.findIndex((u) => u.id === id);
if (checkedIdx === -1) return;
const updatedUser = [...user];
updatedUser[checkedIdx].checked = !updatedUser[checkedIdx].checked;
setUser(updatedUser);
};
const remove = () => {
setUser([...user].filter((u) => !u.checked));
};
return (
<>
<table className="table table-bordered">
<thead>
<tr>
{Object.keys(user[0]).map((elm, i) => {
return (
<td key={i}>{elm.charAt(0).toUpperCase() + elm.slice(1)}</td>
);
})}
</tr>
</thead>
<tbody>
{user.map((elem, ind) => {
return (
<tr key={elem.id}>
<td>{elem.id}</td>
<td>{elem.name}</td>
<td>{elem.surname}</td>
<td>
<input
type="checkbox"
name=""
id=""
onChange={() => toggleCheck(elem.id)}
value={elem.checked}
/>
</td>
</tr>
);
})}
</tbody>
</table>
<button className="btn btn-primary ms-2" onClick={() => remove()}>
Delete selected
</button>
</>
);
};
export default App;
Here is the code to a working sandbox.

Is it possible to dipatsch on useSelector function?

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

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

How to store values of selected checkboxes in array in React js?

I make table contain students names, and checkbox for every row in the table, I want so store the students names that checked in the (students) array. How can I do that?
const [data, setData] = useState([]);
const [students, setStudents] = useState([]);
useEffect(() => {
Axios
.get("http://localhost:3003/studentsnotinsections")
.then(result => setData(result.data));
}, []);
console.log(students)
return(
<div>
<table className="table" >
<thead className="thead-dark">
<tr>
<th scope="col">Name</th>
</tr>
</thead>
<tbody>
{data.map((item, id) => {
return <tr key={id}>
<td>{item.FullName}</td>
<td><input type="checkbox" /></td>
</tr>
})}
</tbody>
</table>
</div>
)}
Did you try simply adding onclick function to your checkbox?
const Compon = () => {
const [data, setData] = useState([]);
const [students, setStudents] = useState([]);
useEffect(() => {
Axios
.get("http://localhost:3003/studentsnotinsections")
.then(result => setData(result.data));
}, []);
const addOrRemove = (name) => {
const newStudents = [...students];
const index = newStudents.indexOf(name);
if (index === -1) {
newStudents.push(name);
} else {
newStudents.splice(index, 1);
}
setStudents(newStudents);
console.log(students)
}
return(
<div>
<table className="table" >
<thead className="thead-dark">
<tr>
<th scope="col">Name</th>
</tr>
</thead>
<tbody>
{data.map((item, id) => {
return <tr key={id}>
<td>{item.FullName}</td>
<td><input type="checkbox" onClick={() => addOrRemove(item.FullName)} /></td>
</tr>
})}
</tbody>
</table>
</div>
)
}
You could use onChange handler, it's more standard for input handling that onClick
import { useCallback, useState } from "react";
export default function App() {
const [data, setData] = useState([
{FullName: 'Alex'},
{FullName: 'Richard'}
]);
const [students, setStudents] = useState([]);
let toggleValue = useCallback((event, id) => {
if (event.target.checked) {
setStudents(value => [...value, id])
} else {
setStudents(value => value.filter(it => it !== id))
}
}, [setStudents])
console.log(students)
return(
<div>
<table className="table" >
<thead className="thead-dark">
<tr>
<th scope="col">Name</th>
</tr>
</thead>
<tbody>
{data.map((item, id) => {
return <tr key={id}>
<td>{item.FullName}</td>
<td><input type="checkbox" onChange={(e) => toggleValue(e, id)} /></td>
</tr>
})}
</tbody>
</table>
</div>
)
}

Remove table row using Hooks

I'm new to react and learning hooks. I found examples online that im converting to use hooks as practice but running into an issue with one. The example i'm converting, I can't figure out why the "Remove" button for function handleRemoveSpecificRow on the table row doesn't work. It should remove the the row. I'm stuck at this point. Any help appreciated.
Demo: https://codesandbox.io/s/zq185w2zkm
import React, { useState } from "react";
import ReactDOM from "react-dom";
import { Table, Button, Input } from "reactstrap";
function App() {
const [rows, setRows] = useState([{}]);
const handleChange = index => e => {
const { name, value } = e.target;
const rows = [...rows];
rows[index] = {
[name]: value
};
setRows(rows);
};
const handleAddRow = () => {
const item = {
column_1: "",
column_2: ""
};
setRows([...rows, item]);
};
const handleRemoveRow = () => {
setRows(rows.slice(0, -1));
};
const handleRemoveSpecificRow = index => () => {
const rows = [...rows];
rows.slice(index, 1);
setRows(rows);
};
return (
<div>
<Table>
<thead className="thead-light">
<tr>
<th>#</th>
<th>Column 1</th>
<th>Column 2</th>
<th />
</tr>
</thead>
<tbody>
{rows.map((item, index) => (
<tr id="addr" key={index}>
<td>{index}</td>
<td>
<Input
type="text"
name="name"
value={rows[index].column_1}
onChange={handleChange(index)}
/>
</td>
<td>
<Input
type="text"
name="mobile"
value={rows[index].column_2}
onChange={handleChange(index)}
/>
</td>
<td>
<Button
outline
color="danger"
onClick={handleRemoveSpecificRow(index)}
>
Remove
</Button>
</td>
</tr>
))}
</tbody>
</Table>
<Button onClick={handleAddRow}>Add Row</Button>
<Button color="danger" onClick={handleRemoveRow}>
Delete Row
</Button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
I too had my fair share of doubts like this, focus towards array/object manipulation techniques. Always manipulate data in a controlled way using unique id, key, etc..
https://codesandbox.io/s/y2lqj9qnjz
import React, { useState } from "react";
import ReactDOM from "react-dom";
import { Table, Button, Input } from "reactstrap";
function App() {
const [rows, setRows] = useState([]);
const handleChange = item => e => {
const { name, value } = e.target;
// const rows = [...rows];
// rows[index] = {
// [name]: value
// };
let items = rows.map(row => {
if (row.id === item.id) {
row[name] = value;
}
return row;
});
setRows(items);
};
const handleAddRow = () => {
let item = {
id: rows.length + 1,
column_1: "",
column_2: ""
};
setRows([...rows, item]);
};
const handleRemoveRow = () => {
setRows(rows.slice(0, -1));
};
const handleRemoveSpecificRow = item => () => {
// const rows = [...rows];
// rows.splice(index, 1);
// setRows(rows);
let items = rows.filter(row => row.id != item.id);
setRows(items);
};
console.log(rows);
return (
<div>
{rows.length != 0 && (
<Table>
<thead className="thead-light">
<tr>
<th>#</th>
<th>Column 1</th>
<th>Column 2</th>
<th />
</tr>
</thead>
<tbody>
{rows.map((item, index) => (
<tr id="addr" key={index}>
<td>{item.id}</td>
<td>
<Input
type="text"
name="column_1"
value={item.column_1}
onChange={handleChange(item)}
/>
</td>
<td>
<Input
type="text"
name="column_2"
value={item.column_2}
onChange={handleChange(item)}
/>
</td>
<td>
<Button
outline
color="danger"
onClick={handleRemoveSpecificRow(item)}
>
Remove
</Button>
</td>
</tr>
))}
</tbody>
</Table>
)}
<Button onClick={handleAddRow}>Add Row</Button>
<Button color="danger" onClick={handleRemoveRow}>
Delete Row
</Button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
https://codesandbox.io/s/y2lqj9qnjz
I've updated the link above.
import React, { useState } from "react";
import ReactDOM from "react-dom";
import { Table, Button, Input } from "reactstrap";
function App() {
const [rows, setRows] = useState([
{
id: 1,
column_1: "",
column_2: ""
}
]);
const handleChange = (item) => (e) => {
const { name, value } = e.target;
// const rows = [...rows];
// rows[index] = {
// [name]: value
// };
let items = rows.map((row) => {
if (row.id === item.id) {
row[name] = value;
}
return row;
});
setRows(items);
};
const handleAddRow = () => {
let item = {
id: rows[rows.length - 1].id + 1,
column_1: "",
column_2: ""
};
setRows([...rows, item]);
};
const handleRemoveRow = () => {
setRows(rows.slice(0, -1));
};
const handleRemoveSpecificRow = (item) => () => {
// const rows = [...rows];
// rows.splice(index, 1);
// setRows(rows);
let items = rows.filter((row) => row.id != item.id);
setRows(items);
};
return (
<div>
{rows.length != 0 && (
<Table>
<thead className="thead-light">
<tr>
<th>#</th>
<th>Column 1</th>
<th>Column 2</th>
<th />
</tr>
</thead>
<tbody>
{rows.map((item, index) => (
<tr id="addr" key={index}>
<td>{item.id}</td>
<td>
<Input
type="text"
name="column_1"
value={item.column_1}
onChange={handleChange(item)}
/>
</td>
<td>
<Input
type="text"
name="column_2"
value={item.column_2}
onChange={handleChange(item)}
/>
</td>
<td>
<Button
outline
color="danger"
onClick={handleRemoveSpecificRow(item)}
>
Remove
</Button>
</td>
</tr>
))}
</tbody>
</Table>
)}
<Button onClick={handleAddRow}>Add Row</Button>
<Button color="danger" onClick={handleRemoveRow}>
Delete Row
</Button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Resources