componentWillReceiveProps render multiple times - reactjs

I am getting datas from my database using three different functions, but as I've seen componentWillReceiveProps is rerendering for three times in this case, which cause duplicating my elements in the frontend. How can I render it just once, or only object's props really change. Till now, my follows[] array objects are duplicating
class UserDashboard extends React.Component {
state = {
uid: this.props.userDetails.uid,
page: 1,
redirect: false,
target: 15,
selectedRole: 4,
selectedSourceRole: 1,
quote_nr: '',
source_id: '',
status_id: '',
cost: '',
rebate: '',
pageLoading: false,
date: '2222-01-02',
therapists:[],
globalTargets:[],
follows:[],
utc: new Date().toISOString().slice(0, 19).replace('T', ' '),
}
topProfilesUrl = 'therapists/top/profiles';
getGlobalTargets = 'owner/targets';
followActivities = 'user/follow/activities';
componentDidMount = () => {
const { getActivities,getFollowActivities,getTarget } = this;
getActivities();
getFollowActivities();
getTarget();
window.scrollTo(0, 0);
}
UNSAFE_componentWillReceiveProps = (newProps) => {
let apiDat = newProps.apiDat;
let apiData = newProps.apiData;
if (apiData.activities && apiData.activities.success ) {
let therapists = apiData.activities.therapists;
let hasMore = true;
console.log("unu")
if (therapists.length < 10) {
hasMore = false;
}
this.setState(() => ({
therapists: this.state.therapists.concat(therapists),
hasMore: hasMore,
pageLoading: false
}))
}
if (apiDat.targets && apiDat.targets.success) {
console.log("doi")
let globalTargets = apiDat.targets.globals;
let hasMore = true;
if (globalTargets.length < 10) {
hasMore = false;
}
this.setState(() => ({
globalTargets: this.state.globalTargets.concat(globalTargets),
}))
}
if (apiData.followActivities && apiData.followActivities.success) {
console.log("trei")
let follows = apiData.followActivities.follows;
let hasMore = true;
if (follows.length < 10) {
hasMore = false;
}
this.setState(() => ({
follows: this.state.follows.concat(follows),
}))
}
}
getTarget = () => {
this.setState({pageLoading: true}, () => { this.loadTargets() })
}
loadTargets = () => {
console.log("load")
this.props.actions.reqGetGlobalTargets({
body: {},
headers: null,
resource: `${this.getGlobalTargets}?page=${this.state.page}`
})
}
getFollowActivities= () => {
this.setState({pageLoading: true}, () => { this.loadFollowActivities() })
}
loadFollowActivities = () => {
console.log("load")
this.props.actions.reqGetFollowActivities({
body: {},
headers: null,
resource: `${this.followActivities}?page=${this.state.page}`
})
}
renderLeads = () => {
return (
this.state.globalTargets.slice(0,1).map( (t, idx) => (
t.daily_leads
))
)
}
renderSales = () => {
return (
this.state.globalTargets.slice(0,1).map( (t, idx) => (
t.daily_sales
))
)
}
renderRatio = () => {
return (
this.state.globalTargets.slice(0,1).map( (t, idx) => (
t.close_ratio
))
)
}
getActivities = () => {
this.setState({pageLoading: true}, () => { this.loadActivities() })
}
loadActivities = () => {
this.props.actions.reqGetTherapistsTopProfiles({
body: {},
headers: null,
resource: `${this.topProfilesUrl}?page=${this.state.page}`
})
}
renderActivities = () => {
const items = this.state.therapists.map( (t, idx) => (
<tr key={t.id} className="activity-display-table">
<td>Quote Nr.: {t.quote_nr}</td>
<td>Source: {t.source_id}</td>
<td>Status: {t.status_id}</td>
<td>Cost: ${t.cost}</td>
<td>Rebate: ${t.rebate}</td>
<td>Date: {t.date.slice(0,10).replace(/-/g,'-')}</td>
</tr>
))
return (
<div ref={0} className="therapist-list">
<h2>Your Past Entries: </h2>
{ items }
</div>
)
}
renderFollowActivities = () => {
const items = this.state.follows.map( (t, idx) => (
<tr key={t.id} className="activity-display-table">
<td>Quote Nr.: {t.quote_nr}</td>
<td>Source: {t.source_id}</td>
<td>Status: {t.status_id}</td>
<td>Cost: ${t.cost}</td>
<td>Rebate: ${t.rebate}</td>
<td>Date: {t.date.slice(0,10).replace(/-/g,'-')}</td>
</tr>
))
return (
<div ref={0} className="therapist-list">
{ items }
</div>
)
}
submitUrl = 'registerActivities';
handleChange = (eve) => {
let inputName = eve.target.name,
value = eve.target.value;
this.setState(() => {
return {[inputName]: value}
})
}
handleSubmit = () => {
this.setState(() => {
const acBody = {
quote_nr: this.state.quote_nr,
cost: this.state.cost,
source_id: this.state.selectedSourceRole,
status_id: this.state.selectedRole,
date: this.state.utc,
rebate: this.state.rebate,
user_id:this.state.uid,
}
this.props.actions.reqActionsUsers(acBody, this.submitUrl);
})
}
handleStatusChange = (event) => {
let statusId = event.target.value;
this.setState(() => ({
selectedRole: statusId
}))
}
handleSourceChange = (ev) => {
let statusId = ev.target.value;
this.setState(() => ({
selectedSourceRole: statusId
}))
}
render () {
console.log(this.state.follows);
return (
<MainWrapper>
<div id="user-dashboard">
<HeaderUser logoutRedirect="/signin"/>
<div className="page-background">
<SidebarUser page="dashboard"/>
{/* Page Content */}
<div className="inner-content">
<div className="top-row">
<h1>Salesperson Dashboard</h1>
</div>
<div className="second-row">
</div>
<div className="activity-table">
<table className="a">
<tr>
<th>Today's Targets ({this.state.utc.slice(0,10).replace(/-/g,'-')})</th>
<th>Weekly Targets</th>
<th>Bonus So Far This Week</th>
</tr>
<tr>
<td>0/{this.renderLeads()} Leads Handled</td>
<td>0/{this.renderLeads()*5} Leads Handled</td>
<td>$0 From Leads</td>
</tr>
<tr>
<td>0/{this.renderSales()} Sales</td>
<td>0/{this.renderSales()*5} Sales</td>
<td>$0 From Sales</td>
</tr>
<tr>
<td>0/{this.renderRatio()} Close Ratio</td>
<td>0/{this.renderRatio()*5} Close Ratio</td>
<td>$0 From Profit Share</td>
</tr>
</table>
</div>
<div>
<h2>Leads Due For A Followup</h2>
{ this.renderFollowActivities() }
</div>
<h2 className="activity">Add Activity</h2>
<div className="activity-menu">
<input type="text"
placeholder="Quote Number"
name="quote_nr"
onChange={this.handleChange}
/>
<select onChange={this.handleSourceChange}>
<option value="1">Phone</option>
<option value="2">Email</option>
<option value="3">Live Chat</option>
</select>
<select onChange={this.handleStatusChange}>
<option value="4">Lead</option>
<option value="5">Sold</option>
</select>
<input type="text"
placeholder="Cost"
name="cost"
onChange={this.handleChange}
/>
<input type="text"
placeholder={this.state.cost/20||("Recom. Rebate" + " $")}
name="recRebate"
readOnly
/>
<input type="text"
placeholder={this.state.cost/10||("Max Possible Rebate" + " $")}
name="maxRebate"
readOnly
/>
<input type="text"
placeholder="Final Rebate $"
name="rebate"
onChange={this.handleChange}
/>
</div>
<ButtonRoundGradient className="activity_button" text="Add Activity" onClick={this.handleSubmit}/>
{ this.renderActivities() }
</div>
</div>
</div>
</MainWrapper>
)
}
}
const mapStateToProps = state => ({
apiData: state.activities,
apiDat: state.targets,
userDetails: state.userDetails
})
function mapDispatchToProps(dispatch) {
return {
actions: {
reqGetGlobalTargets: bindActionCreators(reqGetGlobalTargets, dispatch),
reqGetFollowActivities: bindActionCreators(reqGetFollowActivities, dispatch),
reqGetTherapistsTopProfiles: bindActionCreators(reqGetTherapistsTopProfiles, dispatch),
reqFetchUserDetails: bindActionCreators(reqFetchUserDetails, dispatch),
reqActionsUsers: bindActionCreators(reqActionsUsers, dispatch),
}
};
}
export default connect(mapStateToProps, mapDispatchToProps)(UserDashboard)

ComponentWillRecieveProps would be called every time your state changes or your state changes, so if you want to stop duplicating, you should do this :
componentWillReceiveProps(nextProps, nextContext) {
if (JSON.stringify(nextProps.someProps.items) !== JSON.stringift(this.state.items)){
// do something
}
}
basically you should check if props and state of your component should react to the situation, then really render your application.
hope that helps

Related

React-bootstrap table not refreshed after axios call

I have a table that workig perfectly on page load, that page also include a modal component to update a line of the table, I'm using a custom hook to makes API calls and it works well.
The problem is after a line modification, I dont find the way to refresh the table. The DB get updated, the Toast shows the update success, the call for the new list is fired and got the new list in DBHook with console.log('Result', result) but, the useEffect with heats dependency never get fired.
If I go to another page and come back or do F5, the list refresh properly.
dbHook :
export const useAxios = (url, method, data) => {
const [responseData, setResponseData] = useState(undefined)
const [status, setStatus] = useState(undefined)
const [error, setError] = useState('')
const [loading, setLoading] = useState(true)
const params = {
method: method,
url: url,
headers: { accept: '*/*' },
data: data,
}
const fetchData = async (params) => {
//console.log('in axios', method, url, data)
try {
const result = await axios.request(params)
//console.log('Result', result)
setResponseData(result.data)
setStatus(result.status)
//console.log('resultStatus', result.status)
} catch (error) {
setError(error.response)
//console.log('Error', error)
} finally {
setLoading(false)
//console.log('axios finished', url)
}
}
useEffect(() => {
fetchData(params)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [url])
return { responseData, error, loading, status }
}
Modal :
export default function HeatModal({
isShow,
triggerModal,
activeHeat,
setActiveHeat,
saveHeat,
}) {
function save() {
saveHeat()
triggerModal()
}
return (
<>
<Modal show={isShow} onHide={triggerModal}>
<Modal.Header>
<Modal.Title>{activeHeat.name}</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
<FormGroup as={Row}>
<FormLabel column>
Nouvelle date de chaleur
</FormLabel>
<Col>
<MyDatePicker
field={'date'}
value={activeHeat.date}
setter={setActiveHeat}
/>
</Col>
</FormGroup>
</Form>
</Modal.Body>
<Modal.Footer as={NavContainer}>
<Button variant="outline-success" onClick={triggerModal}>
Annuler
</Button>
<Button variant="success" onClick={save}>
Confirmer
</Button>
</Modal.Footer>
</Modal>
</>
)
}
List :
const HeatList = () => {
const [heatsUrl, setHeatsUrl] = useState('/heats')
const heat = {
idHeat: -1,
name: '',
date: new Date(),
idDog: -1,
}
const [heatsWithName, setHeatsWithNames] = useState([])
const [heatPerPage, setHeatPerPage] = useState(10)
const [firstHeat, setFirstHeat] = useState(0)
const [showModal, setShowModal] = useState(false)
const [activeHeat, setActiveHeat] = useState(heat)
const [editUrl, setEditUrl] = useState('')
const {
responseData: heats,
loading: heatsIsLoading,
error: heatsIsError,
} = useAxios(heatsUrl, 'GET', null)
const { responseData: dogs, loading: dogsIsLoading } = useAxios(
'/dogs',
'GET',
null
)
const { responseData: editResponse, loading: editLoading } = useAxios(
editUrl,
'PUT',
activeHeat
)
useEffect(() => {
console.log('activeHeat', activeHeat.idHeat)
editResponse && console.log('editResponse', editResponse.idHeat)
if (editResponse && activeHeat.idHeat === editResponse.idHeat) {
console.log('in use Effect2')
setActiveHeat(editResponse)
toastSuccess(SAVE_SUCCESS)
setHeatsUrl('/heats')
} else if (editResponse === '') {
console.log('in use Effect3')
toastError(SAVE_ERROR)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [editResponse])
useEffect(() => {
console.log('in new heats array')
let newHeatsArr = []
if (heats && dogs) {
console.log('in if', heats)
heats.map((h) => newHeatsArr.push(buildHeat(h, true)))
}
setHeatsWithNames(newHeatsArr)
console.log('newHeatsArray', newHeatsArr)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [heats, dogs])
const { items, requestSort, sortConfig } = useSortableData(heatsWithName)
const getClassNamesFor = (name) => {
if (!sortConfig) {
return
}
return sortConfig.key === name ? sortConfig.direction : undefined
}
const triggerModal = () => {
setShowModal((prev) => !prev)
}
function buildHeat(origHeat, withName) {
const newHeat = {
idHeat: origHeat.idHeat,
date: origHeat.date,
idDog: origHeat.idDog,
}
if (withName) {
let dogName =
dogs && dogs.find((d) => d.idDog === origHeat.idDog).name
Object.assign(newHeat, dogName && { name: dogName })
}
return newHeat
}
const modifyHeat = (h) => {
setActiveHeat(h)
triggerModal()
}
const saveHeat = () => {
console.log('aH', activeHeat)
setEditUrl('/heat/' + activeHeat.idHeat)
}
return (
<Container>
{heatsIsLoading || dogsIsLoading ? (
<Spinner animation="border" variant="primary" />
) : (
<div className="table-container">
<h1 className="text-center">Liste des chaleurs</h1>
<HeatModal
isShow={showModal}
triggerModal={triggerModal}
activeHeat={activeHeat}
setActiveHeat={setActiveHeat}
saveHeat={saveHeat}
/>
<Paginator
items={items}
dogPerPage={heatPerPage}
setDogPerPage={setHeatPerPage}
firstDog={firstHeat}
setFirstDog={setFirstHeat}
/>
<Table
striped
bordered
hover
responsive
className="table-fixed"
>
<thead>
<tr>
<th>
<button
type="buttton"
onClick={() => requestSort('name')}
className={getClassNamesFor('name')}
>
Nom
</button>
</th>
<th>
<button
type="buttton"
onClick={() => requestSort('date')}
className={getClassNamesFor('date')}
>
Date dernière chaleur
</button>
</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{items &&
items
.slice(
firstHeat,
Number(firstHeat) + Number(heatPerPage)
)
.map((heat) => (
<tr key={heat.idHeat}>
<td>{heat.name}</td>
<td>
{formatDate().format(
new Date(heat.date)
)}
</td>
<td>
<Button className="noBackground noBorder">
<ActionLogoStyle
src={AddLogo}
/>
</Button>
<Button
className="noBackground noBorder"
onClick={() =>
modifyHeat(heat)
}
>
<ActionLogoStyle
src={ModifyLogo}
/>
</Button>
</td>
</tr>
))}
</tbody>
</Table>
</div>
)}
</Container>
)
}
export default HeatList
Thanks for your help.
Thanks Brendan,
This is a real good post that helped me find the solution.
Final hooks :
const dataFetchReducer = (state, action) => {
switch (action.type) {
case 'FETCH_INIT':
return {
...state,
isLoading: true,
isError: false,
}
case 'FETCH_SUCCESS':
return {
...state,
isLoading: false,
isError: false,
data: action.payload,
}
case 'FETCH_FAILURE':
return {
...state,
isLoading: false,
isError: true,
}
default:
throw new Error()
}
}
export const useAxios = (initialUrl, method, initialData) => {
const [url, setUrl] = useState(initialUrl)
const [state, dispatch] = useReducer(dataFetchReducer, {
isLoading: false,
isError: false,
data: initialData,
})
useEffect(() => {
let didCancel = false
const params = {
method: method,
url: url,
headers: { accept: '*/*' },
data: initialData,
}
const fetchData = async (params) => {
dispatch({ type: 'FETCH_INIT' })
console.log('in axios', method, url, params)
try {
const result = await axios(params)
// console.log('result', result)
if (!didCancel) {
dispatch({ type: 'FETCH_SUCCESS', payload: result.data })
}
} catch (error) {
if (!didCancel) {
dispatch({ type: 'FETCH_FAILURE' })
}
}
}
url && fetchData(params)
return () => {
didCancel = true
}
}, [initialData, method, url])
return [state, setUrl]
}
final Modal:
export default function HeatModal({
isShow,
triggerModal,
activeHeat,
setActiveHeat,
saveHeat,
}) {
function save() {
saveHeat()
triggerModal()
}
return (
<>
<Modal show={isShow} onHide={triggerModal}>
<Modal.Header>
<Modal.Title>{activeHeat.name}</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
<FormGroup as={Row}>
<FormLabel column>
Nouvelle date de chaleur
</FormLabel>
<Col>
<MyDatePicker
field={'date'}
value={activeHeat.date}
setter={setActiveHeat}
/>
</Col>
</FormGroup>
</Form>
</Modal.Body>
<Modal.Footer as={NavContainer}>
<Button variant="outline-success" onClick={triggerModal}>
Annuler
</Button>
<Button variant="success" onClick={save}>
Confirmer
</Button>
</Modal.Footer>
</Modal>
</>
)
}
final List:
const HeatList = () => {
const heat = {
idHeat: -1,
name: '',
date: new Date(),
idDog: -1,
}
const [heatsWithName, setHeatsWithNames] = useState([])
const [heatPerPage, setHeatPerPage] = useState(10)
const [firstHeat, setFirstHeat] = useState(0)
const [showModal, setShowModal] = useState(false)
const [activeHeat, setActiveHeat] = useState(heat)
const [heatToSave, setHeatToSave] = useState(undefined)
const [{ data: dogs, isLoading: dogsIsLoading }] = useAxios(
'/dogs',
'GET',
null
)
const [{ data: editResponse, isLoading: editLoading }, setEditUrl] =
useAxios('', 'PUT', heatToSave)
const [{ data: heats, isLoading: heatsIsLoading }] = useAxios(
'/heats',
'GET',
editResponse
)
useEffect(() => {
if (
editResponse &&
activeHeat.idHeat === editResponse.idHeat &&
editResponse > 0
) {
console.log('in use Effect2')
toastSuccess(SAVE_SUCCESS)
} else if (editResponse === '') {
console.log('in use Effect3')
toastError(SAVE_ERROR)
}
}, [activeHeat.idHeat, editResponse])
useEffect(() => {
console.log('in new heats array', heats, 'dogs', dogs)
let newHeatsArr = []
let dogList = []
if (heats && dogs && Array.isArray(heats)) {
dogList = dogs
.filter(({ gender }) => gender === 'Femelle')
.filter(({ status }) => status === 'Élevage')
.filter(({ state }) => state === 'Vivant')
// console.log('in if', dogList)
dogList.map((d) =>
newHeatsArr.push({
idDog: d.idDog,
name: d.name,
idHeat: heats.find((h) => d.idDog === h.idDog)
? heats.find((h) => d.idDog === h.idDog).idHeat
: -1,
date: heats.find((h) => d.idDog === h.idDog)
? heats.find((h) => d.idDog === h.idDog).date
: 'Jamais',
})
)
}
setHeatsWithNames(newHeatsArr)
console.log('newHeatsArray', newHeatsArr)
}, [heats, dogs])
const { items, requestSort, sortConfig } = useSortableData(heatsWithName)
const getClassNamesFor = (name) => {
if (!sortConfig) {
return
}
return sortConfig.key === name ? sortConfig.direction : undefined
}
const triggerModal = () => {
setShowModal((prev) => !prev)
}
const modifyHeat = (h) => {
setActiveHeat(h)
triggerModal()
}
const saveHeat = () => {
console.log('aH', activeHeat)
setEditUrl('/heat/' + activeHeat.idHeat)
setHeatToSave(activeHeat)
}
return (
<Container>
{heatsIsLoading || dogsIsLoading ? (
<Spinner animation="border" variant="primary" />
) : (
<div className="table-container">
<h1 className="text-center">Liste des chaleurs</h1>
<HeatModal
isShow={showModal}
triggerModal={triggerModal}
activeHeat={activeHeat}
setActiveHeat={setActiveHeat}
saveHeat={saveHeat}
/>
<Paginator
items={items}
dogPerPage={heatPerPage}
setDogPerPage={setHeatPerPage}
firstDog={firstHeat}
setFirstDog={setFirstHeat}
/>
<Table
striped
bordered
hover
responsive
className="table-fixed"
>
<thead>
<tr>
<th>
<button
type="buttton"
onClick={() => requestSort('name')}
className={getClassNamesFor('name')}
>
Nom
</button>
</th>
<th>
<button
type="buttton"
onClick={() => requestSort('date')}
className={getClassNamesFor('date')}
>
Date dernière chaleur
</button>
</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{items &&
items
.slice(
firstHeat,
Number(firstHeat) + Number(heatPerPage)
)
.map((heat) => (
<tr key={heat.idDog}>
<td>{heat.name}</td>
<td>
{heat.date === 'Jamais'
? 'Jamais'
: formatDate().format(
new Date(heat.date)
)}
</td>
<td>
<Button className="noBackground noBorder">
<ActionLogoStyle
src={AddLogo}
/>
</Button>
<Button
className="noBackground noBorder"
disabled={
heat.date === 'Jamais'
}
onClick={() =>
modifyHeat(heat)
}
>
<ActionLogoStyle
src={ModifyLogo}
/>
</Button>
</td>
</tr>
))}
</tbody>
</Table>
</div>
)}
</Container>
)
}
export default HeatList

Unable to update nested state react

I have a reusable drop down menu component and i render it twice with two different lists and it should update the state with the id of the first element.
the first drop down of the layout update the state without any issue but the second one does not(i switched the order and it always seems the first one updates the state the second doesn't).
please see code
dashbord
const initializeData = {
actionStatuses: [],
actionCategories: [],
actionGroups: [],
actionEvents: [],
actionEventsWithFilter: [],
selectedFilters: {actionStatusId: "", actionCategoryId:""},
};
const Dashboard = ({ selectedPracticeAndFy }) => {
const [data, setData] = useState(initializeData);
const getSelectedStatus = ({ key }) => {
const actionStatusId = key;
const selectedFilters = { ...data.selectedFilters, actionStatusId };
setData((prevState) => {
return { ...prevState, selectedFilters }
});
};
const getSelectedCategory = ({ key }) => {
const actionCategoryId = key;
const selectedFilters = { ...data.selectedFilters, actionCategoryId };
setData((prevState) => {
return { ...prevState, selectedFilters }
});
};
}
result filter:
const ResultFilter = ({actionStatuses, actionCategories, getSelectedStatus, getSelectedCategory}) => {
return (
<Grid
justify="flex-start"
container
>
<Grid item >
<Typography component="div" style={{padding:"3px 9px 0px 0px"}}>
<Box fontWeight="fontWeightBold" m={1}>
Result Filter:
</Box>
</Typography>
</Grid>
<Grid >
<DropdownList payload={actionCategories} onChange={getSelectedCategory} widthSize= {dropdownStyle.medium}/>
<DropdownList payload={actionStatuses} onChange={getSelectedStatus} widthSize= {dropdownStyle.medium}/>
</Grid>
</Grid>
);
}
DropdownList:
const DropdownList = ({ label, payload, onChange, widthSize, heightSize, withBorders, initialData }) => {
const { selectedData, setSelectedData, handelInputChange } = useForm(
payload
);
useEffect(() => {
if (Object.entries(selectedData).length === 0 && payload.length !== 0) {
setSelectedData(payload[0]);
}
}, [payload]);
useEffect(() => {
if (Object.entries(selectedData).length !== 0) {
onChange(selectedData);
}
}, [selectedData]);
return (
<div style={widthSize}>
<div className="ct-select-group ct-js-select-group" style={heightSize}>
<select
className="ct-select ct-js-select"
id={label}
value={JSON.stringify(selectedData)}
onChange={handelInputChange}
style={withBorders}
>
{label && <option value={JSON.stringify({key: "", value: ""})}>{label}</option>}
{payload.map((item, i) => (
<option key={i} value={JSON.stringify(item)} title={item.value}>
{item.value}
</option>
))}
</select>
</div>
</div>
);
};
It may be a stale closure issue, could you try the following:
const getSelectedStatus = ({ key }) => {
setData((data) => {
const actionStatusId = key;
const selectedFilters = {
...data.selectedFilters,
actionStatusId,
};
return { ...data, selectedFilters };
});
};
const getSelectedCategory = ({ key }) => {
setData((data) => {
const actionCategoryId = key;
const selectedFilters = {
...data.selectedFilters,
actionCategoryId,
};
return { ...data, selectedFilters };
});
};

How do I search data with pagination in React?

class Employee extends Component {
constructor() {
super();
this.state = {
// employees: employees,
employees: [],
searchfield: "",
currentPage: 1,
resultsPerPage: 50,
holder: [],
filteredEmployees: [],
value: "",
};
}
componentDidMount = async () => {
axios.get(`http://localhost:5000/`).then((res) => {
const employees = res.data;
this.setState({
employees: employees.recordsets[0],
holder: employees.recordsets[0],
});
});
};
onSearchChange = (event) => {
let { value } = event.target;
this.setState({ value }, () => {
var updatedList = this.state.holder;
updatedList = updatedList.filter((employee) => {
const fullName = employee.empFirstNm + " " + employee.empLastNm;
return (
fullName.toLowerCase().search(this.state.value.toLowerCase()) !== -1
);
});
this.setState({ employees: updatedList });
});
};
//change page
onPaginate = (pageNumber) => {
this.setState({ currentPage: pageNumber });
};
render() {
const { employees, searchfield, currentPage, resultsPerPage } = this.state;
const { onRouteChange } = this.props;
//Get current employees
const indexOfLastEmployee = currentPage * resultsPerPage;
const indexOfFirstEmployee = indexOfLastEmployee - resultsPerPage;
console.log("indexOfFirstEmployee: ", indexOfFirstEmployee);
console.log("indexOfLastEmployee: ", indexOfLastEmployee);
const filteredEmployees = employees.slice(
indexOfFirstEmployee,
indexOfLastEmployee
);
return !employees.length ? (
<div className="tc">
<h1>Loading</h1>
<Spinner animation="border" variant="danger" />
</div>
) : (
<FadeIn>
<div className="tc">
<div
className="
d-flex
justify-content-between
flex-wrap
flex-md-nowrap
align-items-center
pt-3
pb-2
mb-3
border-bottom"
>
<h1 className="display-2">Employees</h1>
<div
className="tr"
style={{
margin: "15px 0",
}}
>
<NewEmployee employees={employees} />
<SearchBox
searchChange={this.onSearchChange}
value={this.state.value}
/>
</div>
</div>
<Scroll>
<CardList
employees={filteredEmployees}
onDelete={this.handleDelete}
onRouteChange={onRouteChange}
/>
<Table
employees={filteredEmployees}
onRouteChange={onRouteChange}
/>
<Pagination
resultsPerPage={resultsPerPage}
totalResults={employees.length}
onPaginate={this.onPaginate}
/>
</Scroll>
</div>
</FadeIn>
);
}
}
I have tried implementing it with conditions of whether the results
is greater than or equal to the results on a page but it did not
work since it doesnt cover every single case based on the result.
I am getting the search to work but when my current page is anything besides 1 is when the search doesn't work.
Any idea how I can get the filtering to work regardless of what page I
am on?
Figured this out. I just needed to set currentPage to 1 at the end of my search:
onSearchChange = (event) => {
let { value } = event.target;
this.setState({ value }, () => {
//running this after setting the value in state because of async
var updatedList = this.state.holder;
updatedList = updatedList.filter((employee) => {
const fullName = employee.empFirstNm + " " + employee.empLastNm;
return (
fullName.toLowerCase().search(this.state.value.toLowerCase()) !== -1
);
});
this.setState({ employees: updatedList });
this.setState({ currentPage: 1 });
});

How to properly display JSON result in a table via ReactJS

I have this code which fetches data from an API and displays JSON response in a div. How do I display the JSON response in a table?
This is how I display it in div via status.jsx:
// some codings
render() {
const { status, isLocal } = this.props;
if(!isLocal()) {
return (
<div className="status">
<div className="status-text">
<b>Status</b> {status.text}<br />
<b>Title</b> status.textTitle} <br />
<b>Event</b> {status.textEvent}
</div>
</div>
)
}
}
I have tried displaying it in a table as per below but could not get it to be aligned properly
//some codings
render() {
const { status, isLocal } = this.props;
if(!isLocal()) {
return (
<table>
<tbody>
<th>Status</th>
<th>Title</th>
<th>Event</th>
<tr>
<td>{status.text} </td>
<td>{status.textTitle} </td>
<td>{status.textEvent}</td>
<td><button
className="btn btn-danger status-delete"
onClick={this.toggleDeleteConfirmation}
disabled={this.state.showDeleteConfirmation}
>
Delete
</button></td>
</tr></tbody>
</table>
)
}
}
Here is profile.jsx:
import React, { Component } from 'react';
import {
isSignInPending,
loadUserData,
Person,
getFile,
putFile,
lookupProfile
} from 'bs';
import Status from './Status.jsx';
const avatarFallbackImage = 'https://mysite/onename/avatar-placeholder.png';
const statusFileName = 'statuses.json';
export default class Profile extends Component {
constructor(props) {
super(props);
this.state = {
person: {
name() {
return 'Anonymous';
},
avatarUrl() {
return avatarFallbackImage;
},
},
username: "",
statuses: [],
statusIndex: 0,
isLoading: false
};
this.handleDelete = this.handleDelete.bind(this);
this.isLocal = this.isLocal.bind(this);
}
componentDidMount() {
this.fetchData()
}
handleDelete(id) {
const statuses = this.state.statuses.filter((status) => status.id !== id)
const options = { encrypt: false }
putFile(statusFileName, JSON.stringify(statuses), options)
.then(() => {
this.setState({
statuses
})
})
}
fetchData() {
if (this.isLocal()) {
this.setState({ isLoading: true })
const options = { decrypt: false, zoneFileLookupURL: 'https://myapi/v1/names/' }
getFile(statusFileName, options)
.then((file) => {
var statuses = JSON.parse(file || '[]')
this.setState({
person: new Person(loadUserData().profile),
username: loadUserData().username,
statusIndex: statuses.length,
statuses: statuses,
})
})
.finally(() => {
this.setState({ isLoading: false })
})
} else {
const username = this.props.match.params.username
this.setState({ isLoading: true })
lookupProfile(username)
.then((profile) => {
this.setState({
person: new Person(profile),
username: username
})
})
.catch((error) => {
console.log('could not resolve profile')
})
const options = { username: username, decrypt: false, zoneFileLookupURL: 'https://myapi/v1/names/'}
getFile(statusFileName, options)
.then((file) => {
var statuses = JSON.parse(file || '[]')
this.setState({
statusIndex: statuses.length,
statuses: statuses
})
})
.catch((error) => {
console.log('could not fetch statuses')
})
.finally(() => {
this.setState({ isLoading: false })
})
}
}
isLocal() {
return this.props.match.params.username ? false : true
}
render() {
const { handleSignOut } = this.props;
const { person } = this.state;
const { username } = this.state;
return (
!isSignInPending() && person ?
<div className="container">
<div className="row">
<div className="col-md-offset-3 col-md-6">
{this.isLocal() &&
<div className="new-status">
Hello
</div>
}
<div className="col-md-12 statuses">
{this.state.isLoading && <span>Loading...</span>}
{
this.state.statuses.map((status) => (
<Status
key={status.id}
status={status}
handleDelete={this.handleDelete}
isLocal={this.isLocal}
/>
))
}
</div>
</div>
</div>
</div> : null
);
}
}

How to set dynamic data in react?

I have got series of data that contains some objects in one array(json file) and it will be shown by react.
const Library = [
{
name: "Star Wars"
},
{
name: "Harry Potter"
},
{
name: "Lord of the Rings"
},
{
name: "Star Trek"
},
{
name: "The Fault in Our Stars"
},
{
name: "Number the Stars"
},
{
name: "Blue"
},
{
name: "Act Da Fool"
},
{
name: "The Gilded Cage"
},
{
name:
"To Get to Heaven First You Have to Die (Bihisht faqat baroi murdagon)"
},
{
name: "Lebanon"
},
{
name: "Tenderness"
},
{
name: "It"
},
{
name: "Locked Out (Enfermés dehors)"
},
{
name: "Waterloo Bridge"
},
{
name: "Set It Off"
},
{
name: "Nil By Mouth"
},
{
name: "Monte Carlo"
},
{
name: "Treasure of the Four Crowns"
},
{
name: "Donnie Darko"
},
{
name: "Cry-Baby"
},
{
name: "Juan of the Dead (Juan de los Muertos)"
},
{
name: "Constant Nymph, The"
}
];
// Main App Component
class App extends React.Component {
constructor(props){
super();
this.state = {
library: null,
perPage: 1,
currentPage: 1,
maxPage: null,
filter: ""
};
}
componentDidMount() {
this.reorganiseLibrary();
}
// Calculates the library
reorganiseLibrary = () => {
const { filter, perPage } = this.state;
let library = Library;
console.log(library)
if (filter !== "") {
library = library.filter(item =>
item.hotelinfo.hotelsearch.realname.toLowerCase().includes(filter)
);
}
library = _.chunk(library, perPage);
this.setState({
library,
currentPage: 1,
maxPage: library.length === 0 ? 1 : library.length
});
};
// Previous Page
previousPage = () =>
this.setState(prevState => ({
currentPage: prevState.currentPage - 1
}));
// Next Page
nextPage = () =>
this.setState(prevState => ({
currentPage: prevState.currentPage + 1
}));
// handle filter
handleFilter = evt =>
this.setState(
{
filter: evt.target.value.toLowerCase()
},
() => {
this.reorganiseLibrary();
}
);
// handle per page
handlePerPage = (evt) =>
this.setState({
perPage: evt.target.value
}, () => this.reorganiseLibrary());
// handle render of library
renderLibrary = () => {
const { library, currentPage } = this.state;
if (!library || (library && library.length === 0)) {
return <div>No results</div>;
}
return library[currentPage - 1].map(item => (
<div key={item.hotelinfo.hotelsearch.realname}>{item.hotelinfo.hotelsearch.realname}</div>
));
};
render() {
const { library, currentPage, perPage, maxPage } = this.state;
return (
<div className="library">
<h1>Library</h1>
<div className="d-flex">
<div className="flex-fill">
<label className="library__filter-label">Filter</label>
<input value={this.state.filter} onChange={this.handleFilter} />
</div>
<div className="flex-fill text-right">
<label className="library__per-page-label">Per page</label>
<input placeholder="per page" value={this.state.perPage} onChange={this.handlePerPage} />
</div>
</div>
<div className="library__book-shelf">
{this.renderLibrary()}
</div>
<div className="d-flex">
<div className="flex-fill">
{currentPage !== 1 && (
<button onClick={this.previousPage}>Previous</button>
)}
</div>
<div className="flex-fill text-right">
{(currentPage < maxPage) && (
<button onClick={this.nextPage}>Next</button>
)}
</div>
</div>
<div className="library__page-info text-right">
{this.state.currentPage} of {this.state.maxPage}
</div>
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById('root'));
The data of Library is going to be dynamic and the data will be sent from another page to this page by ajax request but as you can see data is set statically. How to set data of Library dynamiclly.
I try below code biy there is this error :
ReferenceError: Library is not defined
Component {
constructor(props){
super();
this.state = {
Library:[],
library: null,
.....
};
$.ajax({
url:"/json.bc",
type:"post",
data:{
cityid:"1182348",
rooms:JSON.stringify({"rooms":[{"adultcount":"1","childcountandage":"0"}]}),
},
success:(result)=>{
this.setState({Library: eval(result)}); } }) }
. . .
}
Edited code :
class App extends React.Component {
constructor(props){
super();
this.state = {
Library:[],
library: null,
perPage: 1,
currentPage: 1,
maxPage: null,
filter: "",
};
}
componentDidMount() {
$.ajax({
url:"/test1.bc",
type:"post",
data:{
cityid:"1182348",
rooms:JSON.stringify({"rooms":[{"adultcount":"1","childcountandage":"0"}]}),
},
success:(result)=>{
this.setState({Library: eval(result)}); }
})}
componentDidMount() {
this.reorganiseLibrary();
}
// Calculates the library
reorganiseLibrary = () => {
const { filter, perPage } = this.state;
let library = Library;
console.log(library)
if (filter !== "") {
library = library.filter(item =>
item.hotelinfo.hotelsearch.realname.toLowerCase().includes(filter)
);
}
library = _.chunk(library, perPage);
this.setState({
library,
currentPage: 1,
maxPage: library.length === 0 ? 1 : library.length
});
};
// Previous Page
previousPage = () =>
this.setState(prevState => ({
currentPage: prevState.currentPage - 1
}));
// Next Page
nextPage = () =>
this.setState(prevState => ({
currentPage: prevState.currentPage + 1
}));
// handle filter
handleFilter = evt =>
this.setState(
{
filter: evt.target.value.toLowerCase()
},
() => {
this.reorganiseLibrary();
}
);
// handle per page
handlePerPage = (evt) =>
this.setState({
perPage: evt.target.value
}, () => this.reorganiseLibrary());
// handle render of library
renderLibrary = () => {
const { library, currentPage } = this.state;
if (!library || (library && library.length === 0)) {
return <div>No results</div>;
}
return library[currentPage - 1].map(item => (
<div key={item.hotelinfo.hotelsearch.realname}>{item.hotelinfo.hotelsearch.realname}</div>
));
};
render() {
const { library, currentPage, perPage, maxPage } = this.state;
return (
<div className="library">
<h1>Library</h1>
<div className="d-flex">
<div className="flex-fill">
<label className="library__filter-label">Filter</label>
<input value={this.state.filter} onChange={this.handleFilter} />
</div>
<div className="flex-fill text-right">
<label className="library__per-page-label">Per page</label>
<input placeholder="per page" value={this.state.perPage} onChange={this.handlePerPage} />
</div>
</div>
<div className="library__book-shelf">
{this.renderLibrary()}
</div>
<div className="d-flex">
<div className="flex-fill">
{currentPage !== 1 && (
<button onClick={this.previousPage}>Previous</button>
)}
</div>
<div className="flex-fill text-right">
{(currentPage < maxPage) && (
<button onClick={this.nextPage}>Next</button>
)}
</div>
</div>
<div className="library__page-info text-right">
{this.state.currentPage} of {this.state.maxPage}
</div>
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById('root'));
You can try this:
getData = (callback) => {
$.ajax({
url:"/json.bc",
type:"post",
data:{
cityid:"1182348",
rooms:JSON.stringify({"rooms":[{"adultcount":"1","childcountandage":"0"}]}),
},
success:(result)=>{
callback(eval(result))
}
. . .
}
}
setData = () => {
this.getData(data => { this.setState({ Library: data }) } )
}
Try calling the ajax call in componentDidMount() set state.
componentDidMount() {
$.ajax({
url:"/json.bc",
type:"post",
data:{
cityid:"1182348",
rooms:JSON.stringify({"rooms":[{"adultcount":"1","childcountandage":"0"}]}),
},
success:(result)=>{
this.setState({Library: eval(result)}); } }) }
. . .
}
}

Resources