I am building a Quiz application using React and I am fetching the questions as an array and maintaining the states:
Array holding all the information about all the questions- statement, options, chosen answer, status(answered, marked for review, unvisited);
object with all information about the current question being displayed
index of current chosen answer (Among the 4 radio buttons)
Now the problem is that when I chose an option and click on 'Save and go to next question' the following events are triggered:
Update current question info (new status and chosen answer)
The array of all questions is updated with the new information for the current question
Go to next question (Update the state #2 (currentQuestionInformation) with the next question
and set #3 to the previously chosen answer for that question (if chosen))
Now the problem is that it is very important for these events to be triggered in sequence else when we go to the next question even if we have answered it and come back to it the state #3 is not updated properly.
In my code since I am using react hooks the state updation is asynchronous and I am not getting the right sequence required. The next question function is triggered before the array with the information about all questions is updated.
I read a lot of answers but all of them suggested using the Effect Hook. But the main problem is I don't have a generalized use case for the setStates.
For example I won't go to the next question necessarily after the total array is updated and this is one particular situation where I need to trigger goToNextQuestion() after state #1 is updated.
What should I do to achieve my required goal?
You can have a single useState hook that has an object with all those states. When updating, you can then update them all at once.
const [myState, mySetState] = useState({questions:[],current:{},selected:0}
Then when updating, copy the old value, destructuring it and replace with the new info
mySetState({...myState, current:newValue, selected:newSelected})
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 12 days ago.
Improve this question
im new to react and I came into a problem I cannot seem to fix,
Im trying to reacreate the iPhones notes app and theres this issue that when I first hit + to add a new note it doesnt get added into the state array, whats weird is that it works normally the second time I click it and so on
My 2nd issue is that theres this "activeObj" state variable which stores the clicked notes object but also normally works on the 3rd try, the first and second clicks look like this (check screenshots)
I tried changing the syntax and the structure of the functions but my skills are still limited since its my first week of doing react
When you call the setState method, it updates the state behind the scenes, and then schedules a re-render of your component with the state variable (e.g. arrayOfNotes) set to the new value. Because you've got your console.log inside the function that's calling the setState method, you're logging the old value, because it hasn't done the re-render yet.
Next render cycle it'll be fine, in fact you could move your console.log into the body of the component and see it'll behave as you're expecting.
When you're updating a state and the new state value depends on the previous one, I'd also recommend using the function version of setState precisely for this reason, e.g.
setArrayOfNotes((prev) => [newNoteObject, ...prev])
This is because prev will take into account other sets done this render cycle, but the way you're doing it currently won't. Doing it this way will certainly save you from other bugs later on.
The cycle order also is what's causing your second issue. You're setting the active note, but because the set won't apply until the next re-render, activeNote will still be the old value in the line below, when you're expecting it to already have been updated. You can just pass id in there instead in this case.
I am wondering if anyone has theories on what to try
One of the devs on my team is using wrapped custom HTML components from another team (the wrapper is basically Robin Weiruch's https://github.com/the-road-to-learn-react/use-custom-element except made into Typescript).
The odd behavior is that the 2nd "select" does not follow the first when the checkbox is checked if and only if the 2nd select had been changed. It will follow the first if the first is changed after the checkbox is checked.
I refactored the code from a useState to a useReducer -- as the wrapped components update, they do cause subsequent change events (and extra dispatch calls) except in the problem scenario [the change event doesn't fire the first time -- it's like its stuck in a debounce]
any theories on what to try?
thanks
useCallback with a warning suppression worked
A better way of dealing with an external component that misbehaves on its understanding of state is still desirable
Good day,
I have read through a number of SO posts which were helpful in progressing my problem, but I still have not found anything concrete information on whether there is an established method for the following scenario I face:
I have an application that allows a user to either:
Add a graph; or
Edit an existing graph
When either the 'add graph' button or the 'edit graph' button is clicked, a side-drawer opens. The fields for for the x-axis label, y-axis label and chart title are either blank (for case (1)) or have fields already populated (for case (2)). The fields themselves are editable text input fields.
Currently, when a graph is added, I create a UUID for the graph and then the user can enter text into the fields, and the click save. When save is clicked a Redux action is dispatched to save the content of the fields in the Redux store. The two-way is binding between the text field values and the local state which contains those values initially. Once the 'save' button is clicked and the Redux action fires, the local state is set to empty strings for each field. So Redux is only used when we actually hit the save button.
Now, when an existing graph is selected, I populate the text fields of the side-drawer with the Redux state via mapStateToProps. I use two way binding that is between the text input fields and the Redux store via Redux actions incorporating debouncing to try reduce the number of actions fired off. This means that essentially a slow typer would cause a Redux action to be fired for every key stroke when editing the fields.
I do not like that we have different methods of two-way binding depending on whether the user clicks 'edit' or 'add'. I also do not like having so many Redux actions fired off for something as simple as adding or removing a letter from a word.
I then went through SO and the Redux documents and found that in general people advised that one should not initialise local state with Redux state via props. What I wanted to do was actually copy the Redux state describing the existing content of the fields (when edit is clicked) into the local state and then have the two-way binding in the local state, similar to what I have for the Case (1) scenario. This then eliminates numerous Redux actions being fired off in quick succession and I can use the same two-way binding irrespective of whether 'add' or 'edit' is clicked. But after reading through the documents it seems this is a bad idea for in the case that there is a an unexpected re-render and the local state is reset mid-way through editing, as well as having two sources of truth.
Having the two-way binding in the local state for an added graph and in Redux for an edited graph is quite confusing and does not provide clean and consistent code for those who will need to maintain this code base. Is there a established method that provides for such a scenario? I imagine editing posts on social media would be a similar context to the problem I am facing.
I have not provided my code as it is not a coding question in itself but rather a design problem and my Redux code is split over several files. But I am happy to provide the code if it helps.
First of all, good question. This is a question that comes up quite a bit, especially with how Redux works. Redux has always, in my opinion, forced the question of, "where should this state live?". Now, I know that the docs do give a pretty clear delineation on what state should live in Redux, but when you begin creating an application, it can still be challenging.
For your particular problem, I would choose one or the other - either use local state for both add and edit or use Redux for both add and edit. To have one in Redux and one in local state is, like you said, confusing. Personally, if there is not a lot of data and not a lot of business logic/computational code, I would use local state. It is okay to use state from Redux to set your initial state, i.e., mapStateToProps -> useState() (if you are using hooks). Once you populate your local state from Redux, the only other time that data reaches Redux is when your component is finished working with that data. I would not be worried about "unexpected re-render and the local state is reset mid-way through editing". If that happens, you have a bug that needs to be fixed and that is not the fault of Redux and passing state to the component.
I hope this helps. Please feel free to ask more questions if you would like or need clarification.
First, thank you for your patience and expertise. Without you, I'd be mopping floors. From Linus Torvalds to DHH to you-- Awesome.
So I have a question duplicate ( answered here-- Why calling setState method doesn't mutate the state immediately? )
I have a 42 radio group form that calls setState for each choice. The above solution says the calls are asynchronous and cause the components to reload. I don't [think I] need to do that. How would you store in memory-- session, cookie -- or something that doesn't cause a repaint until the user submits the form? Sorry if this is another duplicate. I'm slow that way.
Again, thanks.
Not "repainting" the page is not how react works. I am constantly "repainting" the screen as the user interacts with the page I work on.
You want to use setState because if the user keeps clicking around you want to be able to keep updating and tracking what they did, so each update doesn't overwrite. The asynchronous update is beneficial in that you get a copy of the state at the exact moment the user fired off the actions, which ensures you don't end up in a funky state if a user updates a few things at once.
This might be an "asynchronous" update but it feels like it happens immediately and triggers the components to update themselves with the new information (aka repainting)
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
Background info:
I have a function that when called creates select list inside a form and populates it. After that the script runs through the options in the list and looks for a certain value. If the value is there, the script 'selects' that option.
Problem:
Because the list is dynamically created and is some times very large, it takes a while to load. When this happens, the second part of the script (the part that selects an option), does not do anything because the select list has not had time to load.
Idea for a solution:
What would be nice is to call the second part of the function (as a separate function) in an onload event for the select list. But select lists are not supposed to have an onload attribute. The other idea is to simply add a delay, but one day the delay may not be long enough.
How are you doing your AJAX call? Most AJAX libraries will provide the mechanism to do a callback on successful completion. For example in jQuery:
$("#myList").load("ajax.url", function(){
//your content has been loaded, so you can do your selection logic here
});
If you're handling the ajax response manually & building your list in javascript, then you're already have code that knows when the list is finished, so you can just do the selection part once that has finished rather than as a separate function (like as zyeming has suggested).
If that doesn't help you, it might be worth posting some code so people can give you a more specific answer.
Using a delay is not reliable. Whatever you're using to populate the select list should call the function directly when it is finished.
alternately:
Since there is no "onload" event for the select all you can really do it have a function that calls itself after a timeout. If the length of the items in the select list has changed from zero, you know something is currently adding items (the start-point). If the start-point has been reached and nothing has changed after the next timeout, you can assume items have stopped being added to the list, so you can then run the second function.
Why don't you make the function which selects option a callback function. It will be call at the end of the function which creates the list. E.g.
function createList(onComplete) {
// Create the list and maybe other tasks
onComplete();
}
Note: might not be like this but I think you've got the idea...
Ok, I have finally fixed the issue. The solution was completely different than what was discussed here. Basically, I was using 'new Option(value, text)' to add options to my list. I ended up throwing in a if statement and when a value equal what I needed is used new Option(value, text, true). and that solved the problem. All in a day's work.