Why "selected" not rendered here? - reactjs

I am trying to understand how React works with the new Hooks implementation. In this example, I want the browsers to render selected items as I click on the rendered options. But as you can see, it doesn't work.
Here is the example: https://codesandbox.io/s/pjorxzyrx7
Do I have to use the useEffect in this case? Also, as I understand, useEffect couldn't render anything and only return functions. So, what am I missing here?
Thank you!

You're currently mutating the contents of the selected array instead of replacing it. React can't detect a state change when you do this.
Try something like the following:
const handleSelected = item => {
console.log(item);
console.log(selected);
setSelected([...selected, item]);
};
When updating arrays or objects as a part of a state, always make a new copy to assign so that React can properly know when to re-render.
Also, include relevant parts of your code directly in the question in the future, instead of hiding it behind a link (although including a runnable example is great!)

Related

My React state does not update using useState hook with a slightly complexer object

In my React app I purposely combined several arrays in one state using a useState hook. These arrays represent visual objects that I would like to keep "managed" together to ensure that that re-renderings of my application are visually consistent.
While testing I tried to change a property of some objects of my first array. updatedElements reflects the update properly (clearly shown by my console-log). However, updating my useState state does not work. The array elements does not change at all.
Here is the relevant code:
const updatedElements: VisualDiagramElementData[] =
visualData.elements.map((element: VisualDiagramElementData) =>
element.id === id
? { ...element, selected: true }
: { ...element, selected: false }
);
console.log(updatedElements);
setVisualData({
elements: updatedElements,
connectors: visualData.connections,
connections: visualData.connections,
});
What am I missing / doing wrong? Any help is highly appreciated!
When updating a state property based on its previous value, the callback argument of the state setter should be used. Otherwise you may use stale state values.
https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
https://reactjs.org/docs/hooks-reference.html#functional-updates
It should be:
setVisualData((visualData)=>({
elements: visualData.elements.map(/*Your map callback*/),
connectors: visualData.connections,
connections: visualData.connections,
}));
I would go with what is recommended in the React docs - split them out into multiple state variables:
https://reactjs.org/docs/hooks-faq.html#should-i-use-one-or-many-state-variables.
Depending on how you trigger the state updates will determine whether re-render occurs multiple times or not.
See this answer for more details on how the batching works:
https://stackoverflow.com/a/53048903/6854709
Also take note of the comment by aprillion:
Note from github.com/facebook/react/issues/10231#issuecomment-316644950 - This is implementation detail and may change in future versions.
Eventually, it worked by using the "callback argument of the state setter". I revised / reviewed my entire code and I modified all my state setters accordingly. Now, everything works like a charm. Thank you very much again for your help, especially to Nice Books.

How to prevent refresh of list over API after drag & drop with beautiful DND?

I simulated my Context + DND problem in https://codesandbox.io/s/adoring-booth-33vqo . I have other components which will be added to this example and I will use a Context hook to share values across the page.
After the initial render, everything looks fine. The idea of the list is to change the order within itself and when ones changes the order with drag-drop, it throws an "Invalid Hook" error.
So the (first) real question is, what is triggering this error which is linked to the line
const { lang1Library, updateLang1Library } = useContext(LangContext)
;
Thanks in advance for your help.
Geo
It's not a good approach to provide a link for the whole project even if it is small. But I had a quick look and there's at least one thing you're doing wrong:
// DragEndFct.js
export default function DragEndFct(result, libName) {
const { lang1Library, updateLang1Library } = useContext(LangContext);
This is not React component, but it uses a hook - and it is wrong. Hooks have a special meaning in React and should be used properly (Rules of Hooks).
You can't use hooks inside regular functions and expect them to work. That is why you are getting that error.
So, there are many ways you can try to fix this. For instance, DragEndFct is a regular function, you can declare more arguments and pass stuff you get from context:
// you are using it in components right ?
function DragEndFct(result, libName, param3, param4) {}
// so you probably have access to the context there
// and can pass data from the context when you call it.
// something like this
onDragEnd={function (result) {
console.log();
DragEndFct(result, StaticVars.LANG1_LIBRARY_NAME, lang1Library, updateLang1Library);
}}
You could even make DragEndFct to be a React component - it can just return null (which means no UI will be rendered) but in that case you will have hooks and all other stuff there. It really depends on what you need and how you will use it.

How to hide or remove an element onClick in React?

I am trying to hide an element 'GorillaSurfIn' after I click on it.
But also it should fire the 'setShouldGorillaSurfOut' to 'true'. The second part works, but after I added this function:
function hideGorillaSurfIn() {
let element = document.getElementById('gorilla-surf-in');
ReactDOM.findDOMNode(element).style.display =
this.state.isClicked ? 'grid' : 'none';
}
After I click, the code falls apart.
Once I click, the element should be hidden/removed till the next time the App restarts.
Here is the Code Sandbox Link for further explanation.
I am open to any solutions, but also explanations please, as I am still fresh in learning React.
I have changed your code a bit to make it work. You can make further changes according to your need. A few things that I would like to add: -
You should avoid using findDOMNode (in most cases refs can solve your problem) as there are certain drawbacks associated with findDOMNode, such as the react's documentation states "findDOMNode cannot be used with functional components".
I've used refs (forward ref in this case) to make it work.
GorillaSurfIn was called twice, so there were two Gorilla gifs on the screen with same IDs. Not sure if that was the intended behaviour but each element should have unique ID.
Check out the code sandbox.

Redux refresh store after delete comment

I create a widget to add and remove comments. Adding works well. And the removal does not work as needed. When rendering, an empty element appears. Although the reducer passes an array of two objects. Please help me figure it out.
DevTools
JS
Use filter instead.
state.filter(cmt => cmt.id !== action.id)

Store checkbox values as array in React

I have created the following demo to help me describe my question: https://codesandbox.io/s/dazzling-https-6ztj2
I have a form where I submit information and store it in a database. On another page, I retrieve this data, and set the checked property for the checkbox accordingly. This part works, in the demo this is represented by the dataFromAPI variable.
Now, the problem is that when I'd like to update the checkboxes, I get all sorts of errors and I don't know how to solve this. The ultimate goal is that I modify the form (either uncheck a checked box or vice versa) and send it off to the database - essentially this is an UPDATE operation, but again that's not visible in the demo.
Any suggestions?
Also note that I have simplified the demo, in the real app I'm working on I have multiple form elements and multiple values in the state.
I recommend you to work with an array of all the id's or whatever you want it to be your list's keys and "map" on the array like here https://reactjs.org/docs/lists-and-keys.html.
It also helps you to control each checkbox element as an item.
Neither your add or delete will work as it is.
Array.push returns the length of the new array, not the new array.
Array.splice returns a new array of the deleted items. And it mutates the original which you shouldn't do. We'll use filter instead.
Change your state setter to this:
// Since we are using the updater form of setState now, we need to persist the event.
e.persist();
setQuestion(prev => ({
...prev,
[e.target.name]: prev.topics.includes(e.target.value)
// Return false to remove the part of the array we don't want anymore
? prev.topics.filter((value) => value != e.target.value)
// Create a new array instead of mutating state
: [...prev.topics, e.target.value]
}));
As regard your example in the codesandbox you can get the expected result using the following snippet
//the idea here is if it exists then remove it otherwise add it to the array.
const handleChange = e => {
let x = data.topics.includes(e.target.value) ? data.topics.filter(item => item !== e.target.value): [...data.topics, e.target.value]
setQuestion({topics:x})
};
So you can get the idea and implement it in your actual application.
I noticed the problem with your code was that you changed the nature of question stored in state which makes it difficult to get the attribute topics when next react re-renders Also you were directly mutating the state. its best to alway use functional array manipulating methods are free from side effects like map, filter and reduce where possible.

Resources