useState function is not working in functional component - reactjs

I want to change a state (filterConsultantId) with a useState function (setFilterConsultantId) after I trigger a normal function (handleConsultantChange) and I expect the state value is changed when I use the state in another normal function (getActiveLeads). But the state didn't change. Please see my React functional component below:
const TabActiveLeads = ({
...
}) => {
const [filterConsultantId, setFilterConsultantId] = useState('');
//after this arrow function triggered,
const handleConsultantChange = (event) => {
setFilterConsultantId(event.target.value); //and the filterConsultantId should be changed here
//this getActiveLeads function is called
getActiveLeads();
};
const getActiveLeads = () => {
// but the filterConsultantId here is not changed after it should be changed inside handleConsultantChange function
console.log(filterConsultantId);
};
};
export default TabActiveLeads;
I don't understand, why filterConsultantId is not changed inside the getActiveLeads function? Previously it should be changed by calling setFilterConsultantId inside the handleConsultantChange function.
Thanks for any help..

setFilterConsultantId is the asynchronous method, so you can't get the updated value of filterConsultantId right after setFilterConsultantId.
You should get it inside useEffect with adding a dependency filterConsultantId.
useEffect(() => {
console.log(filterConsultantId);
}, [filterConsultantId]);

Related

React - set state doesn't change in callback function

I'm not able to read current state inside refreshWarehouseCallback function. Why?
My component:
export function Schedules({ tsmService, push, pubsub }: Props) {
const [myState, setMyState] = useState<any>(initialState);
useEffect(() => {
service
.getWarehouses()
.then((warehouses) =>
getCurrentWarehouseData(warehouses) // inside of this function I can without problems set myState
)
.catch(() => catchError());
const pushToken = push.subscribe('public/ttt/#');
const pubSubToken = pubsub.subscribe(
'push:ttt.*',
refreshWarehouseCallback // HERE IS PROBLEM, when I try to read current state from this function I get old data, state changed in other functions cannot be read in thi function
);
return () => {
pubsub.unsubscribe(pubSubToken);
push.unsubscribe(pushToken);
};
}, []);
...
function refreshWarehouseCallback(eventName: string, content: any) {
const {warehouseId} = myState; // undefined!!!
case pushEvents.ramp.updated: {
}
}
return (
<Views
warehouses={myState.warehouses}
allRamps={myState.allRamps}
currentWarehouse={myState.currentWarehouse}
pending={myState.pending}
error={myState.error}
/>
I have to use useRef to store current state additionally to be able to rerender the whole component.
My question is - is there any other solution without useRef? Where is the problem? Calback function doesn't work with useState hook?
Your pub/sub pattern does not inherit React's states. Whenever subscribe is triggered, and your callback function is initialized, that callback will not get any new values from myState.
To be able to use React's states, you can wrap refreshWarehouseCallback into another function like below
//`my state` is passed into the first function (the function wrapper)
//the inner function is your original function
const refreshWarehouseCallback =
(myState) => (eventName: string, content: any) => {
const { warehouseId } = myState;
//your other logic
};
And then you can add another useEffect to update subscribe after state changes (in this case, myState updates)
//a new state to store the updated pub/sub after every clean-up
const [pubSubToken, setPubSubToken] = useState();
useEffect(() => {
//clean up when your state updates
if (pubSubToken) {
pubsub.unsubscribe(pubSubToken);
}
const updatedPubSubToken = pubsub.subscribe(
"push:ttt.*",
refreshWarehouseCallback(myState) //execute the function wrapper to pass `myState` down to your original callback function
);
//update new pub/sub token
setPubSubToken(updatedPubSubToken);
return () => {
pubsub.unsubscribe(updatedPubSubToken);
};
//add `myState` as a dependency
}, [myState]);
//you can combine this with your previous useEffect
useEffect(() => {
const pushToken = push.subscribe("public/ttt/#");
return () => {
pubsub.unsubscribe(pushToken);
};
}, []);

Adding state value to JSON object or context

I currently have a context that contains a JS object. I want to be able to add a useState value to this object so that I can later use useEffect on the context to see if the state value has changed.
Here, I have my context:
//This exported as SortContext
export default React.createContext({
sortKeys: [],
setSortKeys: () => {}
})
And here is where I'd like to use the sortKeys array:
export default function useObjectListSort (objectList, sortFunctionMap) {
const sortContext = useContext(SortContext)
useEffect(() => {
//Do something when sortKeys gets updated.
}, sortContext.sortKeys)
And this is a component I made to initialize the context:
export default function SortBlock ({children}) {
const [sortKeyList, setSortKeyList] = useState([])
const sortContextConfig = {
sortKeys: sortKeyList,
setSortKeys: (newKeys) => setSortKeyList(newKeys)
}
return (
<SortContext.Provider value={sortContextConfig}>
{children}
</SortContext.Provider>
)
}
What I would expect is that using the my SortBlock component, and calling the setSortKeys function would update the sortKeyList state, and then the useEffect statement would trigger.
For example:
<SortContext.Consumer>
{sortContext => {
<button onClick={()=>sortContext.setSortKeys(['myKey'])}> Set Keys </button>
}}
</SortContext.Consumer>
Unfortunately, the useEffect is not triggering.
I've figured out the issue. I used the useObjectSortList hook in the same component that I used to initialize the SortBlock. This means that it falls outside of the scope of my context.

does setInterval inside a useEffect can recognize an updated state?

I'm working on an app which requires a user location every X seconds. When the component is mounted, the interval starts and fetch the GPS location and compare it to the old one which is saved on the component state.
The thing is, the interval is comparing only to the default state of the variable, so if the new value is different than the old one, a setter is called to change that value.
I used useEffect on the location var so whenever it changes, to just print it and it does correctly by the new value.
but the interval keeps using the default value given in useState hook.
What is it that I'm missing here.
An example of my code is below:
const [currentLocation, setCurrentLocation] = useState(null);
let locationInterval = null;
useEffect(() => {
locationInterval = setInterval(async () => {
console.log("IN INTERVAL");
navigator.geolocation.getCurrentPosition((location, error) => {
if (location) {
location = [location.coords.latitude, location.coords.longitude];
/// currentLocation is not updating here for some reason
if (JSON.stringify(location) !== JSON.stringify(currentLocation)) {
alert(
`the new location in interval ${location} old location: ${currentLocation}`
);
setCurrentLocation(location);
}
} else {
console.log(error);
}
});
}, 15000);
}, [map]);
useEffect(() => {
return () => {
clearInterval(locationInterval);
};
}, []);
useEffect(() => {
/// currentLocation is updated here from the setInterval
console.log("newlocation", currentLocation);
}, [currentLocation]);
The reason is because currentLocation value is not given as a dependency in the useEffect, therefore that useEffect callback function only has access to the original useState value.
May I ask are there any React warning on your terminal where you run your react app?
Something like
React Hook useEffect has a missing dependency: 'currentLocation'
But on the side node, if you add currentLocation to the dependency,since because you are updating currentLocation in your useEffect and if it's updated, it will re-run the useEffect callback again.
You can not get an updated state in setInterval because callback inside useEffect hook only called when its dependency array updates([map]).
When it is called currentLocation is passed as a parameter to setInterval's callback. Which is the default value for useState at that time.
To prevent this you can do this.
const [currentLocationRef] = useRef(null);
useEffect(() => {
locationInterval = setInterval(async () => {
//....
if (JSON.stringify(location) !== JSON.stringify(currentLocationRef.current)) {
currentLocationRef.current = location;
}
}, 15000);
return () => {
clearInterval(locationInterval);
};
}, [map, currentLocationRef]);
Also, you should return clearInterval in the same useEffect Callback.
The reason behind this is currentLocationRef.current is different from currentLocation. The first one is the getter function and the second one is value. That's why the first one is still able to access updated value while the second one cannot.

how to get state right after state change in react hooks

Hi Everyone I am unable to get current state after setting up the state. I am calling a function Where I am passing that state but I am always getting default state in that function.
this is my State-
const [currentValue , setCurrentValue] = useState(one[0]); // where one[0] is string "Test"
this is my onchange function -
const getTest = useCallback((newValue:string) => {
setCurrentValue({
label:newValue,
value:newValue
});
getCurrentValue(currentValue.value);
getSortOrder()
}, [])
this is my getSortOrder function -
const getSortOrder =() => {
console.log(currentValue);
}
I am not getting updated state in getSortOrder function & getCurrentValue function. But yes If I am trying to console the state in render then it is showing me updated state but in function it is not updating. How can I achieve the updated state in both functions ?
If I get your question correctly then this might help:
const getTest = useCallback((newValue:string) => {
setCurrentValue({
label:newValue,
value:newValue
});
//try this code doing in useEffect
}, [])
useEffect(() => {
getCurrentValue(currentValue.value);
getSortOrder()
},[currentValue]); //Everytime currentValue changes it will give new state
This is happening because the 'setCurrentValue' function is an asynchronous function and the state is updated after both the functions 'getCurrentValue' and 'getSortOrder' have finished their execution.
One easy solution will be to pass the parameter 'newValue' to both the functions like this :
const getTest = useCallback((newValue:string) => {
setCurrentValue({
label:newValue,
value:newValue
});
getCurrentValue(newValue);
getSortOrder(newValue)
}, [])
const getSortOrder =(newValue) => {
console.log(newValue);
}
Or you can use the 'useEffect' functionality to trigger the functions after the value of the state has been updated like this :
const getTest = useCallback((newValue:string) => {
setCurrentValue({
label:newValue,
value:newValue
});
}, [])
useEffect(() => {
getCurrentValue();
getSortOrder()
},[currentValue]);

React Hook - useCustomHook to set outside useState and useRef

I have the main component along with local state using useState and useRef, I also another custom hook, inside the custom hook I would like to reset my main component's state and ref, am I doing correctly by below?
// custom hook
const useLoadData = ({startLoad, setStartLoad, setLoadCompleted, setUserNameRef}) => {
useEffect(() => {
const fetchUser = async() => { await fetchFromApi(...); return userName;};
if (startLoad) {
const newUserName = fetchUser();
setStartLoad(false);
setLoadCompleted(true);
setUserNameRef(newUserName);
}
}, [startLoad]);
}
// main component
const myMainComp = () {
const [startLoad, setStartLoad] = useState(false);
const [loadCompleted, setLoadCompleted] = useState(false);
const userNameRef = useRef("");
const setUserNameRef = (username) => { this.userNameRef.current = username; }
useLoadData(startLoad, setStartLoad, setLoadCompleted, setUserNameRef);
refreshPage = (userId) => {
setStartLoad(true);
}
}
Am I using the custom hook correctly like by passing all external state value and setState method in? Also I found even I don't use useEffect in my customHook, it also works as expected, so do I need to use useEffect in my custom hook? Any review and suggestion is welcome!
First, I think isn't a good approach you use component methods inside custom hook (like "set" methods provided by useState). You are binding the hook with the main component's internal logic. If the purpose of custom hook is fetch data from API, it need to provide to main component the vars that can be able the main component to manipulate its state by itself (like return isFetching, error, data, etc. and don't call any main component set method inside hook).

Resources