My Fixtures.js code:
const [selectedButton, setSelectedButton] = useState(1);
console.log("selected button = ",selectedButton);
const dateClick = (e) => {
const clcikedDivId = e.currentTarget.id;
// some code here!
setSelectedButton(clcikedDivId);
}
};
<div className={classes.roundDatesContainer}>
{roundDates.map((item, index) => (
<Button
id={index + 1}
key={index}
lowOpacity={index + 1 !== selectedButton ? true : false}
className="primary"
customCssStyle={customCssStyle}
click={dateClick}
>
{item}
</Button>
))}
</div>
In my above code snippet, I have a round dates array which I'm iterating over with the map function and for each item I'm outputting a Button component that will have an id property equal to index + 1 and a lowOpacity property of true in case the index + 1 is different from the selectedButton value and false otherwise. In addition it has a click property that tells the button to execute the dateClick function when the button is clicked, and this function will execute some code (that doesn't matter for this question) and at the end of the fucntion the selectedButton value will be set to the clicked button id
My Button component:
function Button(props) {
const { id, type, click, className, lowOpacity, customCssStyle } = props;
console.log("id = ", id, " & low opacity from btn componenet = ", lowOpacity);
return (
<button
id={id}
type={type || "button"}
onClick={
click ||
(() => {
return;
})
}
className={`${classes[className || ""]} ${lowOpacity ? classes.lowOpacity : undefined}`}
>
<span style={{ ...customCssStyle }}>{props.children}</span>
</button>
);
}
As you can see in this component I'm setting a class of lowOpacity to the button in case that prop is set to true (which will be true when index + 1 is different from the selectedButton value).
Now when the page first loads I get the following correct result:
(Note that I have react strict mode on which is why the fixtures.js is loading twice)
Since at first the selectedButton value defaults to 1 and index+1 is equal to 1 that means all buttons except the first 1 will have their lowOpacity property set to true (though there is also an undefined value for a Button with an id of undefined which I have no idea where did that come from).
The problem is when I click on another button all buttons will now have the lowOpacity propert set to true:
For some reason when I click the second (or third) Button the selectedButton changes and become equal to the id of that clicked Button however as you can see from the above image all of the Button components now have their lowOpacity property set to true (plus one of undefined for a button of id undefined which I have no idea where that came from)
The issue here is that IDs are treated as strings in the DOM, so here: index + 1 !== selectedButton (Where selectedButton is set from e.currentTarget.id)
You are comparing a number to a string, and they will never be equal, so !== will always return true
E.g.
function example(e) {
console.log(e.currentTarget.id)
console.log(1 === e.currentTarget.id)
}
<input id="1" onClick="example(event)" placeholder="Click me" />
You will need to either:
Convert the index + 1 to a string before comparing the values
Parse the ID as a number before the comparison
Parse the ID as a number before storing it to selectedButton
I would recommend replacing:
const clcikedDivId = e.currentTarget.id
with
const clcikedDivId = parseInt(e.currentTarget.id)
Related
I'm trying to loop into an array to create the component content, if condition on array element is not satisfied then break the loop and return 1 component
leaves.map(leave => leave.id === currentUser.id ? <div> {leave} </div> : <div> no leaves </div>)
This is the code I have so far, no leaves is printed out every time leave.id is different than currentUser's id
What I need to is, print no leaves only when none of the leaves's id matches the currentUser's id and therefore I want to break the map after printing no leaves
You should use Array.prototype.some. In your case, you would pass a function to the method to check whether it satisfies your condition and use the ternary operator to render the content.
const arrWithId = [670760658, 250026214, 126834449, 987103760, 882536150, 666896331, 488576796, 186598055, 103751309, 419995457, 503676712, 487691896, 744253979, 269253696, 102370148, 237328910, 409016979, 979651614, 743486466, 445993562, 779323321, 939834768, 296731253, 925812473, 114149678];
const arrWithoutId = [123456789, 250026214, 126834449, 987103760, 882536150, 666896331, 488576796, 186598055, 103751309, 419995457, 503676712, 487691896, 744253979, 269253696, 102370148, 237328910, 409016979, 979651614, 743486466, 445993562, 779323321, 939834768, 296731253, 925812473, 114149678];
const id = 123456789;
const checker = elem => id === elem;
console.log(`With ID: ${arrWithId.some(checker) ? "yes" : "no"}`)
console.log(`Array: ${arrWithId}`);
console.log(`Without ID: ${arrWithoutId.some(checker) ? "yes" : "no"}`)
console.log(`Array: ${arrWithoutId}`);
I'm trying to invoke a function call when the user clicks on a button and pass a variable, however when I console.log the param it outputs only one letter as response (so if gallery is passed it only returns g). I'm not sure whether the way the function is being invoked needs to change, or the function receiving it.
const Button = ({selectedIcon}) => {
const [selected] = selectedIcon;
return(
<div className="buttonBlock">
<span className="btn">
<input className="btn-outline" type="button" value="Press Me" onClick={ () => getResponse(selected) }/>
</span>
</div>
);
}
export default function getResponse(selected){
console.log (selected);
if(selected === "gallery"){
console.log (selected);
getGallery();
...
}
With this instruction
const [selected] = selectedIcon;
you are de-structuring the string which is treated as an array and therefore only returns the first character to the variable selected.
More on Destructuring assignments.
I think your problem lies here:
const [selected] = selectedIcon;
if selectedIcon = 'gallery',and [selected] = selectedIcon, then selected = 'g'
> selectedIcon = 'gallery'
'gallery'
> [selected] = selectedIcon
'gallery'
> selected
'g'
>
I have simple list which is dynamically added on add button click. in my list there is a checkbox is also present .so I have an issue when I toggle the checkbox my whole list is re render why ?
let take example I added A,B,C,D in my list when I toggle D checkbox it should only render D item currently it render whole list why ?
here is my code
https://codesandbox.io/s/stupefied-wildflower-gv9be
const Item = ({ text, checked, onCheckedHandler }) => {
console.log(checked, "ssss");
return (
<div className={checked ? "bg" : ""}>
<span>{text}</span>
<input type="checkbox" onChange={e => onCheckedHandler(e, text)} />
</div>
);
};
Every time items changes (whether by adding a new item or checking a value), you are creating a new onCheckedHandler in your App. This propagates down to your Item component. Since the previous onCheckedHandler property is not referentially equivalent to the previous one, it renders (and you see that console log for each item). Memoizing the component alone won't help because a property being passed to it is changing every time.
To get around that, you need to memoize the onCheckedHandler, try this:
const onCheckedHandler = useCallback((e, selectedText) => {
const target = e.target
setItems(items => {
const i = items.findIndex(i => i.text === selectedText);
let obj = items[i];
obj.checked = target.checked;
return [...items.slice(0, i), obj, ...items.slice(i + 1)];
})
}, [setItems])
The you can wrap your Item compoennt with React.memo, and it should work as expected. You'll also need to import the useCallback the same way you import useState
I have a problem with saving the state of the search query.
When the popover is brought into focus, the searchString starts with undefined (second undefined value in picture). When the key 'b' is pressed, the event is fired, and it sets the value to "" (initialized value). As shown, when "bart" is in the search query, console only registers "bar". Does anyone know why this behavior occurs? The end goal is that I am trying to retain the search string on selection (it disappears onclick) -> would appreciate any help with this. The main code block where these changes are happening:
<Autocomplete
open
onClose={handleClose}
multiple
classes={{
paper: classes.paper,
option: classes.option,
popperDisablePortal: classes.popperDisablePortal,
}}
value={pendingValue}
onChange={(event, newValue) => {
setPendingValue(newValue);
}}
// inputValue={searchString}
// onInputChange={(event, newValue) => {
// setSearchString(newValue);
// }}
disableCloseOnSelect
disablePortal
renderTags={() => null}
noOptionsText="No values"
renderOption={(option, { selected }) => (
<React.Fragment>
<DoneIcon
className={classes.iconSelected}
style={{ visibility: selected ? 'visible' : 'hidden' }}
/>
<div className={classes.text}>
{option.value}
</div>
</React.Fragment>
)}
options={[...suggestions].sort((a, b) => {
// Display the selected labels first.
let ai = selectedValue.indexOf(a);
ai = ai === -1 ? selectedValue.length + suggestions.indexOf(a) : ai;
let bi = selectedValue.indexOf(b);
bi = bi === -1 ? selectedValue.length + suggestions.indexOf(b) : bi;
return ai - bi;
})}
getOptionLabel={option => option.value}
renderInput={params => (
<InputBase
ref={params.InputProps.ref}
inputProps={params.inputProps}
autoFocus
className={classes.inputBase}
// onChange={(event) => {
// console.log("event.target: ", event.target);
// console.log("event.currentTarget: ", event.currentTarget);
// setSearchString(event.currentTarget);
// }}
value={searchString}
onChange={handleInputChange}
/>
)}
/>
I have tried to store the value and re-populate it using both through the Autocomplete props and the InputBase (doing it on both causes it to crash). I have added a sandbox for your ref: CodeSandbox
Appreciate all the help!
Material UI autocomplete by design resets the search value every time you select an option. If you want to by pass it, use useAutocomplete hook to fine tune the component according to your need.
As for delayed console log values, you're setting the new value and then you're console logging the old value. So obviously it will print the old value, what else did you expect?
You code should have been like this
const handleInputChange = event => {
// new value => event.currentTarget.value
// old value => searchString
// these values never mutate throughout this function call
setSearchString(event.currentTarget.value);
// searchString still remains the same here and
// won't change even if you call setState
// it remains the same throughout this entire function call
// Since Mutation is not allowed in Functional Programming
// This is perhaps why Functional Programming is
// far better than Object Oriented Programming 😉
console.log('searchString: ', event.currentTarget.value);
}
However this isn't the right way to observe state changes. Better way would be something like this,
// This will be called whenever React
// observes a change in anyState
useEffect(() => {
console.log(anyState)
}, [anyState])
I'm creating a football betting app, where I'd like to pick a winner/loser/draw from a match, and then store that in a list of selected bets.
What I have so far
For each match, you can select a winner of either team or a draw. The getSelection method is passed down as props to the onClick handler of each button.
The getSelection method then adds the value of the button click to an array, like so:
getSelection = (val: object) => {
this.setState(
{bets: [...this.state.bets, val]},
() => console.log(this.state.bets, "bets")
);
};
I want only to be able to make a single selection for each match, represented visually like so:
Codesandbox.
This solution is updating the Match Results based on the User's selection.
SINGLE MATCH WILL HAVE SINGLE RESULT, i.e RESULTS WILL OVERWRITE
Also to keep track of Match Number, I have used matchNumber as an index.
Your getSelection will look something like this.
getSelection = (val: object, matchNumber: number) => {
// 1. Make a shallow copy of the items
let bets = [...this.state.bets];
// 2. Make a shallow copy of the item you want to mutate
let bet = { ...bets[matchNumber] };
// 3. Replace the property you're intested in
bet = val;
// 4. Put it back into our array. N.B. we *are* mutating the array here, but that's why we made a copy first
bets[matchNumber] = bet;
// 5. Set the state to our new copy
this.setState({ bets }, () => console.log(this.state.bets));
Update the background:
<button
style={{ background: winner == home.name ? "lightblue" : "white" }}
onClick={() => this.props.getSelection(home, matchNumber)}
>
{home.name}
</button>
<button
style={{ background: winner == draw.name ? "lightblue" : "white" }}
onClick={() => this.props.getSelection(draw, matchNumber)}
>
{draw.name}
</button>
<button
style={{ background: winner == away.name ? "lightblue" : "white" }}
onClick={() => this.props.getSelection(away, matchNumber)}
>
{away.name}
</button>
Check this working solution. https://codesandbox.io/s/9lpnvx188y
Are you saying you should only be able to add a single possibility once?
Then this might work:
getSelection = (val: object) => {
this.setState( {
bets: [...this.state.bets, val].filter((value, index, self) => { return self.indexOf(value) === index; })
},
() => console.log(this.state.bets, "bets")
);
};
(That is, take the entire array, add the new value, filter so you only get the distinct values, and store it in bets).
ala: https://codesandbox.io/s/x2o5m95oxw