Probably it is a classic issue with useState which is not updating.
So there is a tree with some checkboxes, some of them are already checked as they map some data from an endpoint.
The user has the possibility to check/uncheck them. There is a "cancel" button that should reset them to the original form.
Here is the code:
const [originalValues, setOriginalValues] = useState<string[]>([]);
...
const handleCancel = () => {
const originalValues = myData || []; //myData is the original data stored in a const
setOriginalValues(() => [...myData]);
};
...
useEffect(() => {
setOriginalValues(originalValues);
}, [originalValues]);
However, it is not working, the tree is not updating as it should. Is it something wrong here?
Just do the following, no need for ()=> the state will update inside the hook if called, plus change the constant it will cause confusion inside your code and protentional name clash later on, with the current state variable name, and also make sure your data are there and you are not injection empty array !!!! which could be the case as well !.
// Make sure data are available
console.log(myData)
// Then change the state
setOriginalValues([...myData]);
Related
I am trying to determine if a customer has an active subscription or not. To do this I am utilizing the following code:
const stripe = require('stripe')('some-api-key');
export default function Example(){
// the user will automatically be considered non-subbed by default
const [isSubscriber, setIsSubscriber] = useState(false)
// grab the customer id from stripe
async function get_customer_id() {
const customers = await stripe.customers.search({
query: `metadata[\'some-meta-data-key\']:\'some-meta-data-value\'`
});
return customers.data[0]['id']
}
// grab the list of active subscriptions from stripe
async function customer_is_subscriber(){
const subs = await stripe.subscriptions.list({
status: 'active',
});
return subs
}
// determine if the customer id is in the list of active subscriptions.
// return true if so, false otherwise
async function test_equality(){
const customer_id = await get_customer_id();
const subbed = await customer_is_subscriber();
const answer = subbed.find(sub => sub.customer === customer_id)
return !!answer;
}
useEffect( () => {
async function load_result() {
const promise_function_return = await test_equality()
setIsSubscriber(promise_function_return)
}
load_result();
}, [isSubscriber]);
return (
// some react code
)
}
I have been able to successfully get all of my other functions where I am doing the comparisons for if a user is a subscriber but where I am having an issue is updating the state value (e.g. true if they are subbed, false otherwise).
I found some good past questions on this specific topic such as:
here The useState set method is not reflecting a change immediately
here: setState inside Promise in React
and here: setState inside a Promise function in a useEffect with hooks?
but I just have not been able to get it working correctly. This is currently the closest I have been able to get to solving this problem.
Currently your code says that, when isSubscriber changes, it should check if the user is a subscriber and update the isSubscriber state... so it's a chicken and egg problem. It won't set isSubscriber until isSubscriber gets set.
I think you want to change }, [isSubscriber]); to }, []); so that that code executes when the component first loads (not when isSubscriber changes).
The useEffect hook will always run on mount regardless of if there is anything in its dependency array. This means that your useEffect will work as is, and will run onMount as well as when isSubscriber changes:
useEffect( () => {
async function load_result() {
const promise_function_return = await test_equality()
setIsSubscriber(promise_function_return)
}
load_result();
}, [isSubscriber]);
To verify this, you can check out this codesandbox example. The useEffect looks just like yours, and you will notice that isSubscriber is initially set to false, but is updated to true after 3 seconds.
There's still an adjustment you may want to make even though that part appears to work ok. With isSubscriber in the dependency array, the function in your useEffect will be called any time isSubscriber changes. This probably not what you want, since this function doesn't actually depend on isSubscriber, but actually sets isSubscriber. In this case, that means test_equality() will be run on initial mount and then one more time after it sets isSubscriber, which is unnecessary.
This blog post explains the useEffect dependency array really well.
You can fix this by removing isSubscriber from the dependency array, like this:
useEffect(() => {
console.log("in useEffect");
async function load_result() {
const promise_function_return = await test_equality();
setIsSubscriber(promise_function_return);
}
load_result();
}, [isSubscriber]);
Since you mentioned the state value is not getting updated, there must be another issue going on in either get_customer_id() or customer_is_subscriber(). It would be good to double check and make sure the stripe api calls are working as expected.
I'm trying to re-run the useEffect hook based if any new item has been added to the localstorage.
My useEffect looks like this :
useEffect(() => {
//getting all localstorage items and setting to a variable
const localStorageItems = { ...localStorage }
// filtering out the localstorage items for keys that only starts with "HTTP" & pushing it to the state called "testData" using setTestData useState hook.
const filteredByKey = Object.fromEntries(
Object.entries(localStorageItems).filter(([key, value]) => {
if (key.startsWith("http")) {
testArr2.push({ urls: [key, value] })
// setTestData({ urls: [key, value] })
setTestData((prev) => [...prev, { key, value }])
}
})
)
}, [])
My problem is when I hit a button(adding a new data) this useEffect should ideally re-run, setting the latest data to the "testData" state.
The Problem :
I cannot pass window.localstorage to the useEffect's array dependency list, as it is external variable I believe and react doesn't allow it.
What I have tried:
I have looked at this answer on SO, but my situation is a bit complex as I'm loading the entire items to a variable first(I'm not aware of any better alternatives)
I have tried to put the state value in the dependency list, but it doesn't work and goes to infinite loop.
Please help.
Thanks for reading this far.
i can give you a work around for this use case, create react context that syncs with localStorage. you can use the context as a useEffect dependency. sync means, take the value from localStorage at starting and update the context along with the localStorage from your app.
Define a state that holds the local storage value and pass it as a useEffect parameter.
const [localStorageData, setLocalStorageData] = useState([]);
useEffect(() => {
// Your codes
}, [localStorageData])
you can use 'storage' event listener
window.addEventListener('storage', function(e) {
if (e.newValue) {
console.log('new data is saved ...')
// ...... your code
}
})
I have an array that shows a set of items attached to a user. The array checks it first if the current user is on the item's inventory and then displayed as 'user name' and 'date borrowed' on a table. The adding feature is done in a modal, and suppose to update the table.
The problem is everytime I add, delete or update, the table doesn't update at all. Also this table is an expandend component of another table (react-data-table-component)
Here is the useState, and useEffect of my table:
const InventoryTable= ({
selectedUser,
items,
getItems,
getUsers
}) => {
useEffect(() => {
getItems();
getUsers();
}, []);
const [data, setData] = useState([]);
useEffect(() => {
let data= [];
data= items?.filter((item) =>
item?.users.some(
(user) => parseInt(user?.id) === parseInt(selectedUser?._id)
)
);
setData(data);
}, []);
Note: selectedUser, is the user from the main table that was selected and this current table is to show the itms attached to it.
If I add data on the setData(data); }, []); it crashes.
Adding data, selectedUser, and items on the dependency arrays loads it non-stop that causes to crash the page
useEffect method takes 2 parameters as input.
callback
dependency
If the dependency is empty array it will be called in the similar way as Class Component with componentDidMount and componentWillUnmount lifecycle methods.
if dependency is present then the callback will be called after the UI in painted.
So clearly you have missed required dependency
I'm not sure that i understand the whole concepts of your code because it is just a small part of the code. But useEffect() will run accordingly if you want it to run as ComponenentDidMount you will use the code that you said above however in your case you want to update delete add it means you want to detect the change in data so you need to include data within the brackets like this
`useEffect(() => {
let data= [];
data= items?.filter((item) =>
item?.users.some(
(user) => parseInt(user?.id) === parseInt(selectedUser?._id)
)
);
setData(data);
}, [data,items,selectedUser]);`
Seems you forgot to pass items and selectedUser to effect dependency array.
Maybe I am wrong, but have you tried renaming data property inside useEffect? It should have problem, that you are setting state without set callback.
Try also set useState default value to [] instead of {} (object).
I have a function inside of my functional component that uses a value saved in state. However, when it is called, it has the original value in state, not the updated value. When I look at my component in Chrome React Dev Tools, I see that the updated value is stored in state. Aren't functions supposed to get the latest state value in React? I didn't think I'd have to wrap my functions in a useEffect every time some value in state they depend on changes. Why is this happening?
const Editor = (props) => {
const [template, setTemplate] = useState(null);
const [openDialog, setOpenDialog] = useState(false);
useEffect(() => {
if (props.templateId) {
getTemplate(props.templateId));
}
},[]);
const getTemplate = (templateId) => {
{...make API to get template...}
.then((response) => {
if (response.template) setTemplate(response.template);
});
}
/* THIS FUNCTION SAYS TEMPLATE IS ALWAYS NULL */
const sendClick = async () => {
if (template) {
await updateTemplate();
} else {
await initializeTemplate();
}
setOpenDialog(true);
};
}
UPDATE: I figured out the issue. The sendClick function is being used inside an object that I have in state. When that object is created, it creates a version of the sendClick function based on the state at that time. I realized I needed to refactor my code so that the function is not stored within my object in state so that the function will always have the latest state values.
Please correct the code there its setTemplate(template)); not getTemplate(template));
I'm guessing that you have that right in the source code... if Yes then,
You have got into a trap that all developers new to React fall into.
This code is betraying you ...
useEffect(() => {
if (props.template) {
setTemplate(template)); // Mentioned as getTemplate(template));
}
},[]); // Here is where you make the mistake
The second argument you pass to the useEffect is called as Dependencies. Meaning if your useEffect is dependent on any state or any variable or function, Ii should be pass as the second argument inside the []. By now you should have got the answer.
Clearly, your useEffect is dependent on template. You should pass that inside the [].
So the code will be : -
useEffect(() => {
if (props.template) {
setTemplate(template)); // Mentioned as getTemplate(template));
}
},[template]);
Now React will automatically run the function every time the value of template changes therefore, updates template.
For more information about useEffect ...
Refer React Documentation
Refer the useEffect API
I am using React Hooks to manage states within a component.
const addNode = () => {
let pform = pForm
let handles = [vForm, yForm, hForm]
let access_info = [virtualForm, management1Form, management2Form, consoleForm]
let newObj = {
...currentForm,
p: pform,
handles: handles,
access_info: access_info,
}
console.log('newObj', newObj)
setCurrentForm(
newRouterObj
)
console.log(currentForm)
let currArr = [...addedNodes]
currArr.push(currentForm)
setAddedNodes(currArr)
intializeForms()
}
The function above is an onClick that I use when I press an Add button. The forms (pForm, vForm, yForm, etc.) are all separate states. I gather them together and put them into a single object newObj and use setCurrentForm to update the currentForm state to newObj.
When I console.log the newObj, everything goes in fine. However, when I check the currentForm after the setCurrentForm, the fields (p, handles, and access_info) are empty.
I know that states in React can have a delay in updates so I might have to use useEffect. However, in my use case, which is to gather different states and put them in as a new field in the currentForm state seems useEffect is not the best way to solve it. Can anyone help please?
You are misunderstanding exactly how useState works. When you call the useState setter function, the state value isn't actually updated immediately, instead it will trigger the component to re-render with the updated value. Even though you call the setter half way through the function, the state value will remain the original value for the entire lifetime of that function call.
You could slightly tweak what you have to be
const addNode = () => {
...
let currArr = [...addedNodes]
// you know that currentForm is supposed to be newObj, so just push that
// see my explanation above to understand why it currentForm isn't what you expect
currArr.push(newObj)
...
}
It's an async action so values will not be assigned/updated instantly. You need to watch for the changes using useEffect hook to log new values and to do anything in case
useEffect(() => {
// Whenever `currentForm` will be updated, this callback will be invoked
console.log('updated currentForm values', currentForm);
},[currentForm]);