Radio button onChange event not firing if a new element is added - reactjs

Hope you are well, I stumbled upon an issue that I do not understand and before looking any further into the (pretty convoluted) code I inherited, I want to check with you if this is an expected behaviour.
{Object.keys(locations).map((keyName) => {
const { name: territoryName, code: territoryCode } = locations[keyName];
return (
<RadioButton
key={`LGRB${territoryName}`}
style={{ margin: '10px 0' }}
onChange={() => console.log('onChange')}
checked={territoryCode === selectedTerritoryCode}
name={territoryName}
label={territoryName}
dark
rightAlign
/>
);
})}
All works fine and as expected, it renders a series of radio buttons with selectable locations, onChange triggers as expected.
If I now push a new element to locations, it correctly renders the new element but:
First time a user clicks on any of the radio buttons, the onChange event doesn't trigger
Second time a user clicks on any of the radio buttons, the onChange event now triggers
Why?
Thank you for any help
Edit:
This is the <RadioButton> component:
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import { RadioButton as GrommetRadioButton } from 'grommet';
const RadioButtonWrapper = styled.div`
StyledComponentsStyles omitted
`;
const RadioButton = ({ dark, rightAlign, ...props }) => (
<RadioButtonWrapper dark={dark} rightAlign={rightAlign}>
<GrommetRadioButton {...props} />
</RadioButtonWrapper>
);
RadioButton.propTypes = {
dark: PropTypes.bool,
rightAlign: PropTypes.bool,
};
export default RadioButton;

I think there is an issue with how the state of the checked RadioButton is being handled. To remove the clutter of handling this state and these strange side-effects coming from the rendering of individual RadioButton, try to see if RadioButtonGroup is solving your problem, it will be more straightforward to control the RadioButton(s) input events and state management.
Just for a POC, here is an example code that uses RadioButtonGroup, with dynamically added RadioButton items that react (pun intended) as expected to onChange events.
import React, { useState } from "react";
import { render } from "react-dom";
import { grommet, Box, Grommet, RadioButtonGroup } from "grommet";
export const App = () => {
const [fruits, setFruits] = useState(["pear", "banana", "mango"]);
const [value, setValue] = useState(fruits[0]);
const [counter, setCounter] = useState(0);
const handleOnChange = (event) => {
console.log(event.target.value);
setValue(event.target.value);
setFruits([...fruits, "apple" + counter]);
setCounter(counter + 1);
};
return (
<Grommet theme={grommet}>
<Box pad="small">
<RadioButtonGroup
name="radio"
options={fruits}
value={value}
onChange={(event) => handleOnChange(event)}
/>
</Box>
</Grommet>
);
};
render(<App />, document.getElementById("root"));
I'm not sure if that is the answer you were hoping for, but consider it as best practice advice in case you are planning to refactor the current behavior. Good luck!

Related

Rendered Component doesn't show in storybook

I want to render MUI Button Component in my storybook and then change MUI attributes(variant, color, disabled, etc.) from storybook. I could get result when working on simple plain html button but it doesn't work on MUI one. The current result is it doesn't show any error in terminal, nor in browser. When I type something in args it's visible on Storybook Controls but Button itself doesn't show up itself. Here is the code of MyNewButton.tsx file.
import Button from "#mui/material/Button"
export const MyNewButton = () => {
return <Button />
}
and here is the code of MyNewButton.stories.tsx
import { ComponentMeta, ComponentStory } from "#storybook/react"
import { MyNewButton } from "./MyNewButton"
export default {
title: "MyComponents/MyNewButton",
component: MyNewButton,
} as ComponentMeta<typeof MyNewButton>
const Template: ComponentStory<typeof MyNewButton> = (args) => (
<MyNewButton {...args} />
)
export const First = Template.bind({})
First.args = {
label: "MyNewButton",
backgroundColor: "red",
}
and Here is the storybook screenshot.
So, I can see you are overlooking some basic steps and I'm going to point them out to you. Your Storybook is rendering fine.
First, you are rendering a button from MUI without a label/text. You are supposed to have an opening tag and a closing tag for the button.
so, it should be <Button>Hello World!</Button> and not <Button />.
After fixing that, the next step is to use your args in your MyNewButton component so that storybook will know what to change your background color to.
import { FC } from "react";
type ButtonProps = {
backgroundColor: string;
label: string;
};
export const MyNewButton: FC<ButtonProps> = (props) => {
const { backgroundColor, label } = props;
const style = {
backgroundColor,
};
return (
<Button variant="contained" style={style}>
{label}
</Button>
);
};
Your button should be rendering fine correctly now and to make change the background in your app, you just have to pass in the props like so:
<MyNewButton backgroundColor={"green"} label={"My New Button"} />

How to focus and select a checkbox using React ref?

I have been looking around a method to correctly focus and select a checkbox in React code.
The methods focus() and select() that I'm using in the example below are not working :
import React, { useRef } from "react";
export const HelloWorld = () => {
const checkboxref = useRef(null);
const handleOnClick = () => {
checkboxref.current.focus();
checkboxref.current.select();
};
return (
<div>
<button onClick={handleOnClick}>Focus</button>
<input type="checkbox" ref={checkboxref} />
</div>
);
};
When I click on the button, my checkbox is not focused and not selected...
Any solution please ?
Thank you so much.
You don't need to create a separate function to handle onChange event
const checkboxref = useRef(null);
You can simply get the current value of the checkbox with:
checkboxref.current.checked
// which returns a boolean
use this one it might help you. here I am using createRef instead of useRef and also uses the callback hook which ensures the availability of ref when you click the button.
import React,{createRef, useCallback} from 'react';
export const HelloWorld = () => {
const checkboxref = createRef();
const handleOnClick = useCallback(() => {
const node = checkboxref.current;
if(node){
node.focus();
node.select();
}
}, [checkboxref]);
return (
<div>
<button onClick={handleOnClick}>Focus</button>
<input type="checkbox" ref={checkboxref} />
</div>
);
};
The select methods selects text in elements such as text inputs and text areas, so I'm not sure what effect you expect it to have on the checkbox. As for focus, it can focus, but again, there is not much you can do with a focused checkbox. I can only think of styling it https://jsfiddle.net/4howanL2/1/

React - two buttons - a click on one of them opens both

I have a React button component with onClick handler and its state - stating whether it was opened on click. If I render two of such components in a wrapper container, and I click on one of them, both buttons update the state. How should I handle the state, so that only one of the buttons updates without using ids?
import React, {useState} from 'react';
const Button = (props) => {
const [open, setOpen] = useState(false);
const text = open ? 'Open' : 'Closed';
const toggleButton = () => { setOpen(!open) };
return (
<button onClick={toggleButton}>{text}</button>
)
}
// parent component
import React from 'react';
import Button from './Button'
const ButtonsWrapper = () => {
return (
<div>
<Button />
<Button />
</div>
)
}
I also tried reversing the logic and putting the state in a wrapper component, and then passing the onClick handler as a props to a button, but the sitation is the same. They both change the state at the same time when one of them is clicked.
I am using React Hooks.
My understanding is that you are saying that when you click one button both buttons seems to have their state updated, but you only want the clicked button to update its state.
(i.e. if you click Button A only that button will show 'Open' as its text, Button B will continue to show closed)
If the above is right, then your code should already do the correct thing. If it doesn't then you might have a bug elsewhere that would cause this.
If however you want to click one button and BOTH should switch state then you could achieve this by keeping track of the state in the parent component:
import React, {useState} from 'react';
const Button = (props) => {
const text = props.isOpen ? 'Open' : 'Closed';
const handleClick = () => {
// do some stuff you want each button to do individually
.....
props.onClick()
}
return (
<button onClick={handleClick}>{text}</button>
)
}
// parent component
import React from 'react';
import Button from './Button'
const ButtonsWrapper = () => {
const [buttonState, setButtonState] = useState(false)
return (
<div>
<Button onClick={() => setButtonState(!buttonState)} isOpen={buttonState} />
<Button onClick={() => setButtonState(!buttonState)} isOpen={buttonState} />
</div>
)
}

Changing button label on click with grommet?

Does anyone have any insight into changing button label on click when using grommet UI and styled components? Is it easier to just use a custom button rather than grommets?
Here are a few examples on how to make the Button label change onClick action:
import React, { useState } from "react";
import { render } from "react-dom";
import { grommet, Box, Button, Grommet, Text } from "grommet";
const App = () => {
const [label, setLabel] = useState(1);
const [name, setName] = useState("shimi");
const flipName = name => {
return name === "shimi" ? setName("shai") : setName("shimi");
};
return (
<Grommet theme={grommet}>
<Box pad="small" gap="small" width="small">
// label is a number that is being increased on every click event
<Button
label={label}
onClick={() => {
setLabel(label + 1);
}}
/>
// label string is controlled with external logic outside of the button.
<Button
label={<Text weight="400">{name}</Text>}
onClick={() => {
flipName(name);
}}
/>
</Box>
</Grommet>
);
};
render(<App />, document.getElementById("root"));
In addition to the examples above, in Grommet, you don't have to use the label prop and you can leverage the Button children to control the way your Button display.

Ant-Design React-Native Search Bar Cancel Button cannot hide itself when pressed

I'm trying to make the Cancel button on the right of the search bar hide when it gets press. I tried to use to showCancelButton prop but it is not working. Ant-Design React-Native Docs Link
This is the link to the issue reproduction repo https://github.com/kvnlee777/antd-rn-issue
react-native version 0.61.4
ios simulator version 12.4
import React, {useState} from 'react';
import { Alert, View} from 'react-native';
import { SearchBar } from '#ant-design/react-native';
const SearchBarDemo = () => {
const [value, setValue] = useState('');
const [showCancel, setShowCancel] = useState(true);
const onChange = (currentValue) => {
setValue(currentValue);
}
const clear = () => {
setValue('');
setShowCancel(false);
}
return (
<View style={{ marginTop: 30}}>
<SearchBar
value={value}
placeholder="Search products, brands"
onSubmit={value => Alert.alert(value)}
onCancel={clear}
onChange={onChange}
cancelText='Cancel'
showCancelButton={showCancel}
/>
</View>
);
};
export default SearchBarDemo;
You are using strings for true and false. The string 'false' evaluates to true.
Replace 'true' and 'false' with true and falseand it should work

Resources