React.js displayed values not matching parent state and rendered HTML - javascript

I'm making a to-do list type app to learn React. I have an App component, which contains a Todolist component, which contains ListItem components. Each ListItem component has a title and a boolean completed which is controlled by a checkbox.
ListItem components that are checked and completed are sorted to the bottom of the Todolist. When an item is checked, I call a function setItemCompleted() (passed down in props from App) to update App's state and re-render everything.
In setItemCompleted() I print the updated array of ListItem and it matches what I expect it to be. When React re-renders everything and I open the Chrome inspector to check the rendered HTML, it also matches what I expect it to be. The problem is, the actual ListItem that are displayed on the page do not match either of these; instead of the checked off item moving to the bottom of the page, the last item in the list gets checked.
Here is a "minimal, complete, and reproducible" example:
https://codesandbox.io/s/react-playground-su7iw?file=/index.js

You use index as ListItem key which is not recommended and in your case causes these issues. Change from key={i} to key={item.itemID} and it should fix these issues.

Related

After removing DOM element from child component when refreshing props gets error

CODE
https://github.com/bostonmacosx/MTBCRM/tree/main/mtbcrm_react/src/components
PARENT COMPONENT: MainScreen
Child Components: Userlist on the left and People next to it.
I have a list which when clicked sends the information to the child components.
When I then click on the X I run a javascript routine in the child component which removes the phone number from the database which works fine....
When I go to click on one of the names in the List....which run the get userDetails in the MainScreen component and updates the props for the people component I get
Not sure why the react app is erroring out at this piont. It was fine until I removed the phone number DIV with javascript after clicking the "X"
Not sure why.

Component re-render on state change not happening

I have a react app similar to what is shown in this codesandbox link https://codesandbox.io/s/eatery-v1691
Clicking the Menu at the bottom center of the page renders the MenuFilter component.
The MenuFilter component in turn uses a checkbox component to render several check boxes & the checked status of these checkboxes depends on the selectedMeals prop that I pass to the MenuFilter in app.tsx.
I read that when the state is passed as a prop, the re-rendering happens as soon as the state changes but this is not happening.
The selectedMeals state is initialized with allItems and then managed such that when any other items is click, that items is set & allItems and these individual items can be several at once. The state management works fine but the checkbox is not getting updated based on the state!
If you however, click cancel (ok button does not do anything yet) & then click Menu button again, only the values in the selectedMeals prop are rendered.
See the video here (https://youtu.be/Zmfc0MQGaIU this is the full app, codesandbox app is representation of this but not the same) where the state change (allItems gets removed & when allItems is checked other items get removed) works perfectly but the check boxes are not re-rendering appropriately
What am I doing wrong & more importantly how do I get the result I want?
I initially put the question here re-render as soon as the state is changed but there was no sandbox for it & since the sandbox is different from what I put in that question, I created a new question. Thanks.
In this video https://youtu.be/2v3lOPaIPzU I have captured the change in the selectedMeals in console log & I also show how the checkboxes does not match the selectedMeals.
The problem is in the Checkbox component.
const [checked, setChecked] = useState(isChecked);
isChecked will only be used as the initial value, checked will not update every time isChecked changes. You could manually update checked in a useEffect hook with isChecked as a dependency, but it is unnecessary as the Checkbox component can be implemented without any state.
If you however, click cancel (ok button does not do anything yet) &
then click Menu button again, only the values in the selectedMeals
prop are rendered.
That's because the modal is conditionally rendered based on menuModalIsShown and when this boolean gets toggled, the component unmounts and mounts back causing useState(isChecked) to use the updated state.
Updated Checkbox:
export const CheckBox = ({
standAlone,
text,
onCheck,
isChecked,
value
}:
CheckBoxProps) => {
return (
<div
className={`${styles.checkbox} ${
standAlone ? "" : styles.notStandAlone
} `}
>
<input
type="checkbox"
checked={isChecked}
onChange={onCheck}
value={value}
/>
<label htmlFor={styles.checkbox}>{text}</label>
</div>
);
};

Weird re-render behavior with react material-ui dialog

Background
So I build a front-end project for a delivery service, based on React and Material UI.
I was asked to use a Dialog window that will be opened when clicking on item, and there the user will have the opportunity to customize the item.
The dialog can be seen here(very simple: item photo, name, desc, and customization options):
https://ibb.co/GFkpy8Q
(Sorry for the pixelization)
The problem
I use react hooks in this project, and that why manage the Dialog's state.
Although simple, I stumbled upon few problems with how elements got re-rendered/not re-rendered(when expected):
The "checked" prop of the checkboxes uses Array.some, to see if the unique ID of the checkbox is in the state Array. The checkboxes are not being set to checked when clicking on them. The onChange prop simply pushes the checkbox's value to the state array and sets the state:
const [array, setArray] = React.useState([]);
...
<Checkbox
checked={
array.some(
item => item._id === subOption._id
)
}
onChange={() => setArray(array.push(subOption))}
/>
The onChange action works properly, so why the "checked" prop doesn't work properly?
When a checkbox is checked, I want to add a small quantity field next to it, so the user will be able to choose the quantity of the subOption he shall receive.
So I use the next well-practiced pattern:
{array.some(item => item._id === subOption._id) &&
(
<QuantityField />
)
}
The problem is that the QuantityField is not shown after the checkbox get checked.
If I exit the Dialog and enter it again, I suddenly see the checked checkbox is checked, and the QuantityField is shown next to it 😕
If I change the item's quantity with the general QuantityField you can see at the bottom left of the Dialog image, suddenly all the checked checkboxes gets unchecked 😕
The general QuantityField uses a state of it's own, and is not connected to any of the checkboxes.
From what I could see after I tried to debug the weird behavior, I can say that the render action of the Dialog component isn't working as expected. The states are updated, but doesn't trigger a re-render of the Dialog tree. Actually, it is wrong to say that, as the Dialog tree gets re-rendered, but the "checked" prop is not being re-checked during the re-render; but a complete un-mount and re-mount of the Dialog shakes the tree right.
Hope for an interesting answer. Thank you.
I would change how you are using setArray. See from Array.prototype.push() documentation:
The push() method adds one or more elements to the end of an array and returns the new length of the array.
Also using .push() on the state is not allowed because never should mutate the state directly.
Suggested solution instead:
onChange={() => setArray(prevState => [...prevState, subOption])}

Issues on animating a conditionally rendered component using React-spring

I am new with react-spring. I am having trouble animating a component whenever it unmounts. I have a simple card with an onClick handler that's responsible for conditionally displaying my Overlay component. The animation works fine when mounting (from & enter works), but when closing the overlay, the component just disappears without animation (leave does not work). I suspect it's because of the conditional rendering of the component but I've been struggling for hours trying to find a solution for this one. Any help would be appreciated!
My current code: https://codesandbox.io/s/dry-leftpad-h3vmv
What I'm trying to achieve: https://codesandbox.io/s/048079xzw
P.S. The latter is using mauerwerk's lib. I don't want to use that.
What you were missing is this:
return expand.map(({ item, props, key }) => (
item && <animated.div
// ...etc
When you're controlling the mounting of a single component with useTransition, you need to conditionally render it based on the item being passed. In your case, when it's false it won't render (which will unmount if already mounted) and when it's true it will render (mount if unmounted).
Here's a working sandbox forked from yours: https://codesandbox.io/s/infallible-agnesi-cty5g.
A little more info
The first argument to useTransition is the list you want to transition. That watches for changes and sends back an array mapped with each item, a key and a style object (props) based on whether the item is truthy (entering) or falsy (leaving). So for a transition that mounts/unmounts a single element, conditionally rendering based on the truthiness of the item is key.
Check out the examples again here and you'll see the differences between transitioning a list, a toggle between two elements, and a single item.
For a list, no need to check for the existence of the item because the array changes.
For toggling between two elements, you use the truthiness of item to determine which element to render.
For a single element, item determines whether to render at all. This means it won't mount initially when you default to false, and will make sure you don't render 2 items whenever your isActive value changes.

React how to change focus of inner component's input box from the parent

I have a child component that renders several list elements from an array. This child component's list items when clicked turns to input boxes, and can be changed. But I need to also put focus on the input box when it's rendered.
Here is a quick view of what my app is so far.
I'm making list items from an array in the state and it's then mapping around while populating the array items in the parent components render function (like any other tutorial shows).
render: function() {
var listItems = this.state.ingredients.map(function(item) {
return <ListItem key={item.id} id={item.id} refer={item.ref} handleChange={this.toggleEnv} env={item.env} ingredient={item.text} />;
}.bind(this));
id for callback from the child component to search the array using id.
env for toggling states from list item to input box and back to list item.
toggleEvn is the function i use for every manipulation of the child component with callback a child component make.
Now that refer property where I pass the item.ref to the child component so it can attach it to the input and then use
componentDidMount: function() {
ReactDom.findDOMNode(ref-name-passed-from-child-component-callback).focus();
}
Where in the child element i pass clicked list items ref (which is equal to input box's ref) to the callback so it can be captured by the parent but it passes null so i can't add the focus from ReactDom.findDOMNode().
I checked in react documentation and they say something like
Never access refs inside of any component's render method – or while any component's render method is even running anywhere in the call stack
I'm a very newbie in react and i think this is referring to my current situation.
But they also have another way of using refs by passing a callback in ref which will be used when some button is clicked as seen here https://facebook.github.io/react/docs/more-about-refs.html
I followed the example by
Adding a ref callback like this ref={(ref) => this.myTextInput = ref}
in my input tag.
Instead of the button, i triggered the function when the list item is clicked. This doesn't work since the this.myTextInput isn't created yet. (Since this function is triggered when list item is clicked and it's before the input is rendered.
At this point i was stuck completely because i can't pass this ref to check it in componentDidMount.
I'm convinced that I'm missing on some understanding here please with refs, child components and callback. Please help.

Categories

Resources