Extract Select Menu values from modal [discord.js] - discord.js

I'm currently making a modal based ordering system and extracting the TextInput works fine, however I can't seem to figure out how to extract the SelectMenu data, as the current code only returns an error
TypeError: interaction.fields.getSelectMenuValue is not a function
client.on('interactionCreate', async interaction => {
if (!interaction.isModalSubmit() || !interaction.customId === 'tankform') return;
await interaction.reply({ content: 'Your order was received successfully!', ephemeral: true });
const IGN = interaction.fields.getTextInputValue('minecraft_ign');
const Weapon = interaction.fields.getSelectMenuValue('weapon_type');
const ownedWeapon = interaction.fields.getSelectMenuValue('owned_weapon');
const ownedTanks = interaction.fields.getSelectMenuValue('owned_tanks');
const wantedTanks = interaction.fields.getSelectMenuValue('wanted_tanks');
console.log({IGN, Weapon, ownedWeapon, ownedTanks, wantedTanks})
});

Your code looks fine. The problem is that discord.js doesn't support recieving select menus from modals yet. I suggest you use discord-modals. It works for me personally.

This bit
if (!interaction.isModalSubmit() || !interaction.customId === 'tankform') return;
the 2nd comparison is incorrect, you want
if (!interaction.isModalSubmit() || interaction.customId !== 'tankform') return;
instead

Try:
else if (interaction.type === InteractionType.ModalSubmit) // fixed
{
if(interaction.customId === 'yourId') {
const IGN = interaction.fields.getTextInputValue('minecraft_ign');
const Weapon = interaction.fields.getSelectMenuValue('weapon_type');
const ownedWeapon = interaction.fields.getSelectMenuValue('owned_weapon');
const ownedTanks = interaction.fields.getSelectMenuValue('owned_tanks');
const wantedTanks = interaction.fields.getSelectMenuValue('wanted_tanks');
console.log({IGN, Weapon, ownedWeapon, ownedTanks, wantedTanks})
}

Related

How to declare multiple variable useRef with a loop? Is that possible

I need to clean up the code to be DRY from my senior.
Initially my logic and code is already working but senior want me to improve my code.
My code is something like this
const firstTextInputRef = useRef(null);
const secondTextInputRef = useRef(null);
const thirdTextInputRef = useRef(null);
const fourthTextInputRef = useRef(null);
const fifthTextInputRef = useRef(null);
const sixthTextInputRef = useRef(null);
const onOtpKeyPress = index => {
return ({nativeEvent: {key: value}}) => {
// auto focus to previous InputText if value is blank and existing value is also blank
if (value === 'Backspace' && otpArray[index] === '') {
if (index === 1) {
firstTextInputRef.current.focus();
} else if (index === 2) {
secondTextInputRef.current.focus();
} else if (index === 3) {
thirdTextInputRef.current.focus();
} else if (index === 4) {
fourthTextInputRef.current.focus();
} else if (index === 5) {
fifthTextInputRef.current.focus();
}
}
};
};
What I am doing now is I am listening to keypress and change the focus of the keyboard from 1 input to another.
What he want me to do is I need to declare the ref inside a loop and improve the if else statement
This snippet is what he suggest.
let otpTextInputs = [];
for(let i = 0; ;i!=5;i++) {
// put new ref into the array
}
I already tried multiple way and spend more that 2 hours on it and I still don't know how to do it. Can anyone suggest an answer?
You can just create a useEffect that runs when you first initialize your component that generates the inputs programmatically and sets their ref. That way you can set up a state where you can store all the refs and then access them.
Here is a CodeSandbox demo with the proposed solution.

react setState not updating state in one of my functions

I'm working an a react app with a few forms and I am trying to implement an edit form for input items. The function first opens the list item in a pre-populated form.
The editItem function currently looks like this:
editItem(event) {
event.preventDefault();
const target = event.target.parentNode.parentNode;
const { key } = target.dataset;
const { className } = target;
const currState = { ...this.state[className] };
const currItem = currState.list[key];
for (let i in currItem) {
if (i !== "list" && i !== "hidden") {
currState[i] = currItem[i]
}
}
this.setState({ [className]: currState });
this.hideUnhide({target: {name: className}});
}
I have confirmed with console logs that currState is correctly set with the values that I am looking for, and that I am not having an async issue. I am using this same format to set state in other functions in my app and all of the others are working properly. If I directly mutate state in the same place, I get the behavior I'm looking for (form fields populate), but nothing happens when I use setState.
Link to my github repo: here. The function in question is in App.js.
As Brian Thompson points out in his comment, it turns out that the hideUnhide function call directly after my setState uses setState as well and writes over the first setState call with the previous state:
hideUnhide(event) {
const { name } = event.target;
const currState = { ...this.state[name] };
if (currState.hidden === true) {
currState.hidden = false;
}
this.setState({ [name]: currState });
}
The way to prevent that was to use hideUnhide as a callback to the setState in editItem:
this.setState({ [className]: currState }, () =>
this.hideUnhide({ target: { name: className } })
);
and now everything functions as intended.

how do we push a checkbox to an array while checkbox is by default checked in react js?

the checkbox by default checked if this checkbox is by default checked then it should be automatically pushed into addtess_type array.
https://react-jkrrcb.stackblitz.io demo
handleInputChangeDom = (event) => {
const target = event.target;
var value = target.value;
const Address_type = this.state.Address_type;
if (event.target.checked) {
Address_type.push(event.target.value);
} else {
let index = Address_type.indexOf(event.target.value);
Address_type.splice(index, 1);
}
this.setState({
Address_type: Address_type,
});
};
I think you are trying to maintain a state of all the checked items.
One advice would be to use Set instead of an array. It will make life so much more easier for you.
const handleInputChangeDom = (event) => {
const newSet = new Set(this.state.Address_type); // create a clone, don't update current state
if(event.target.checked) {
newSet.add(event.target.value);
} else {
newSet.delete(event.target.value);
}
this.setState({ Address_type: newSet });
}
One suggestion - please try to use camelCase with React, that is suggested.

Leafet setLatLng unmounts and mounts markers continuously, preventing events from firing

I'm trying to visualise 500+ vehicles using leaflet. When the position of a marker (vehicle) changes, it will move slowly to reach the destination (using requestAnimationFrame and leaflet's 'native' setLatLng since I don't want to update the state directly). It works well, but I also have a click listener on each marker and notice that it never fires. I soon realised that leaflet has been updating the marker continuously (the DOM element keeps blinking in the inspector). I attempted to log something to see if the component actually re-renders, but it doesn't. Seems like leaflet is messing with the DOM element under the hood.
const Marker = React.memo(function Marker({ plate, coors, prevCoors }) {
const markerRef = React.useRef();
const [activeVehicle, handleActiveVehicleUpdate] = useActiveVehicle();
const heading = prevCoors != null ? GeoHelpers.computeHeading(prevCoors, coors) : 0;
React.useEffect(() => {
if (prevCoors == null) return;
const [prevLat, prevLong] = prevCoors;
const [lat, long] = coors;
let animationStartTime;
const animateMarker = timestamp => {
if (animationStartTime == null) animationStartTime = timestamp;
const progress = (timestamp - animationStartTime) / 5000;
if (progress > 1) return;
const currLat = prevLat + (lat - prevLat) * progress;
const currLong = prevLong + (long - prevLong) * progress;
const position = new LatLng(currLat, currLong);
markerRef.current.leafletElement.setLatLng(position);
requestAnimationFrame(animateMarker);
};
const animationFrame = requestAnimationFrame(animateMarker);
// eslint-disable-next-line consistent-return
return () => cancelAnimationFrame(animationFrame);
}, [coors, prevCoors]);
React.useEffect(() => {
if (plate === '60C23403') console.log('re-render!');
// eslint-disable-next-line
});
return (
<LeafletMarker
icon={createIcon(plate === activeVehicle, heading)}
position={prevCoors != null ? prevCoors : coors}
onClick={handleActiveVehicleUpdate(plate, coors)}
ref={markerRef}
>
<Tooltip>{plate}</Tooltip>
</LeafletMarker>
);
});
How do I prevent this behaviour from leaflet? Any idea is appreciated. Thanks in advance :)

What is proper way to call function from onClick so it don't trigger wrong one?

When I click on button then onClick triggers correct function, run half through and jumps to other function which is not related to it and run through half of it and jumps back to first function, runs half trough again and drops error
Uncaught TypeError: _this.state.searchValue.toLowerCase is not a function
Interesting part is that I click other button before which triggers this function with toLowerCase() and there is no errors.
I dont have any idea whats going on here but so far i was trying to remove few lines to see which line cause it because I dont think that line with toLowerCase() realy is the reason. Everything works when I remove lines where is first this.setState.
Here is my function:
( Alerts is used to track where function is at, that how i know
that it run half through only. It never reach alert("DDD").
This function is which is triggered with button onClick like it should be )
onSelect = (e) => {
const data = e.target.getAttribute('data-id');
const itemId = e.target.getAttribute('data-id');
const itemIdState = !this.state[e.target.getAttribute('data-id')];
alert("AAA")
this.setState(state => { // <--- Somehow problem comes from this setState function
const newState = {};
for (const dataId in state) {
newState[dataId] = dataId === data
}
alert("BBB")
return newState
});
alert("CCC")
this.setState(State => ({
[itemId]: itemIdState,
}), function() {
alert("DDD")
if(this.state[itemId] === true){
this.setState({isAnySelected: true})
}else if(this.state[itemId] === false){
this.setState({isAnySelected: false})
}
})
}
This is other function which is triggered by mistake and is not related to other. It is just returning component which is displayed and when I press on its button then i have this issue.
filterSearch = (id, title, path) => {
let name = title.toLowerCase()
let filter = this.state.searchValue.toLowerCase()
if(name.includes(filter)){
return <SearchResult key={id} data-id={id} pName={path} onClick={this.onSelect} selected={this.state[id]} />
}
}
And here is from where filterSearch is triggered. Behind this.props.searchResult is Redux.
{this.props.searchResult ? this.props.searchResult.map(category =>
this.filterSearch(category.id, category.title, category.path)
) : null
}
I think I see what the problem is: in your problematic this.setState, you cast everything in your state to a boolean:
this.setState(state => {
const newState = {};
for (const dataId in state) {
newState[dataId] = dataId === data
}
alert("BBB")
return newState
});
Your for() statement ends up comparing searchValue to data (some kind of ID), which I imagine more often than not will not be the case, so searchValue ends up getting set to false.
And what happens when you try to do .toLowerCase() on a Boolean?
To fix this, consider structuring your state like this:
this.state = {
searchValue: '',
ids: {},
};
Then, replace your problematic this.setState with something like this:
this.setState((state) => {
const newIDs = {
// Create a clone of your current IDs
...state.ids,
};
Object.keys(newIDs).forEach(key => {
newIDs[key] = key === data
});
alert("BBB")
return {
// searchValue will remain untouched
...state,
// Only update your IDs
ids: newIDs,
}
});
What exactly are you wanting to do here?
this.setState(state => {
const newState = {}; // You are initializing an object
for (const dataId in state) {
newState[dataId] = dataId === data // You are putting in an array every property of state that is equal to data
}
return newState
});
So irrevocably, your this.state.searchValue property will be changed to something else, which is of boolean type. So toLowerCase being a function for string.prototype, you will get an error.
You should describe what you where aiming to get here.

Resources