Reputation: 5255
I have created a function component with a dropdown. Everytime the user selects a new value i send an event to consuming component. I am using hooks for the selected index. I realized that hooks works async. When a new value was selected the old value was posted to the consuming component. So I started using "useEffect". However useEffect has that side effect that it is called when the variable is initialized. Is there anyway to tell REACT not to send an event to useEffect on initialization so I dont send a selected change event on initialization? Do you just add a boolean variable (like hasBeenInit) or is there something smarter?
https://codesandbox.io/s/blissful-clarke-ky1nr?fontsize=14&hidenavigation=1&theme=dark
import React, { useState, useEffect } from "react";
import {
Dropdown,
DropdownToggle,
DropdownMenu,
DropdownItem,
Button
} from "reactstrap";
const DropdownPaging = props => {
const [selectedValue, setSelectedValue] = useState(10);
const [dropdownOpen, setDropdownOpen] = useState(false);
const [pageIndex, setPageIndex] = useState(0);
const toggle = () => setDropdownOpen(prevState => !prevState);
function dropDownChanged(val) {
setSelectedValue(val);
}
useEffect(() => {
triggerEventChange(selectedValue, pageIndex);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedValue, pageIndex]);
function pageIndexIncremented() {
setPageIndex(counter => counter + 1);
}
function pageIndexDecremented() {
if (pageIndex >= 1) {
setPageIndex(counter => counter - 1);
} else {
setPageIndex(0);
}
}
function triggerEventChange(take, index) {
if (props.valueChanged) {
props.valueChanged({ take: take, pageIndex: index });
} else {
}
}
return (
<div>
<table>
<tr>
<td>
<Button
outline
disabled={pageIndex === 0}
color="dark"
onClick={pageIndexDecremented}
>
<
</Button>
</td>
<td>{pageIndex}</td>
<td>
<Button outline color="dark" onClick={pageIndexIncremented}>
>
</Button>
</td>
<td>
<Dropdown isOpen={dropdownOpen} toggle={toggle}>
<DropdownToggle caret outline color="dark">
{selectedValue}
</DropdownToggle>
<DropdownMenu>
<DropdownItem onClick={() => dropDownChanged(10)}>
10
</DropdownItem>
<DropdownItem onClick={() => dropDownChanged(25)}>
25
</DropdownItem>
<DropdownItem onClick={() => dropDownChanged(50)}>
50
</DropdownItem>
<DropdownItem onClick={() => dropDownChanged(100)}>
100
</DropdownItem>
</DropdownMenu>
</Dropdown>
</td>
</tr>
</table>
</div>
);
};
export default DropdownPaging;
Upvotes: 2
Views: 2962
Reputation: 2714
You could use useRef
to simulated whether the component has mounted or not, so your component becomes :
const mounted = useRef(false);
const toggle = () => setDropdownOpen(prevState => !prevState);
function dropDownChanged(val) {
setSelectedValue(val);
}
useEffect(() => {
if (mounted.current) {
triggerEventChange(selectedValue, pageIndex);
} else {
mounted.current = true;
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedValue, pageIndex]);
updated sandbox: https://codesandbox.io/s/dropdownpager-urvrp
Upvotes: 5
Reputation: 1159
The boolean value is an option. If you need the behavior in other places in your app you can create a custom hook:
const useEffectIgnoringInit = (callBack, watchedValues) => {
const [hasBeenInit, setHasBeenInit] = useState(true);
useEffect(() => {
if(hasBeenInit){
callBack();
} else {
setHasBeenInit(true);
}
}, watchedValues);
}
Then you use it instead of useEffect:
useEffectIgnoringInit(() => {
triggerEventChange(selectedValue, pageIndex);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedValue, pageIndex]);
Upvotes: 1