I've got a useState variable that has an array of JSON objects, and I'm trying to get the text fields to dynamically render and update with onChangeText, but I'm having a bit of an issue with this.
When I add console.log(index) to updateHashtags, it says it is undefined, and I can't understand what I'm doing wrong or can do to make this work. In theory, the index should be a static number for each text field. So the first text field would have hashtags[0] as its value and use '0' as the index for updating the state of hashtags.
When I use:
console.log('index',index);
inside of updateHashtags, I get:
index undefined
Here's the code:
const updateHashtags = (text, index) => {
let ht = hashtags;
ht[index].name = text;
setHashtags(ht);
}
const hashtagElement = (
<>
<Text style={styles.plainText} >Set Your Values:</Text>
<Text style={styles.instructionsText} >This is what other users use to search for you.</Text>
{hashtags.map((e,index) =>
<TextInput
placeholder='value'
key={e.name + index}
value={hashtags[index].name}
onChangeText={(text,index) => updateHashtags(text,index)}
style={styles.textInput}
/>
)}
<TouchableOpacity
onPress={() => {
let ht = hashtags;
let newht = {
name: '',
weight: 0,
};
ht[ht.length] = newht;
setHashtags(ht);
}}
>
<Ionicons
name="add-circle-outline"
size={40}
color={'black'}
/>
</TouchableOpacity>
</>
);
Maybe the onChangeText functions doens't get index param.
Try it like this:
// we are getting index from here
{hashtags.map((e,index) =>
<TextInput
placeholder='value'
key={e.name + index}
value={hashtags[index].name}
// so no need of taking index from param here
onChangeText={(text) => updateHashtags(text,index)}
style={styles.textInput}
/>
)}
Also for your updateHashTags functions consider this insted:
const updateHashtags = (text, index) => {
// doing it this way ensures your are editing updated version of state
setHashtags((state) => {
let ht = state;
ht[index].name = text;
return ht;
});
}
Related
I am new to react native and have a situation where I am trying to set a state to a filtered version of another state(array). With the current code, each item in the unfiltered array is being changed to the filtering condition. How can I setFilteredJobs to only contain 'jobs', where status equals the status that the user has chosen in the AppPicker?
Here is my code:
const [jobs, setJobs] = useState()
const [filteredJobs, setFilteredJobs] = useState()
const [status, setStatus] = useState()
const handleStatusChange = (item) => {
setFilteredJobs(
jobs.filter( job => job.status = item.label )
)
setStatus(item)
}
return (
<View style={defaultStyles.screenNoPadding}>
<AppTextInput placeholder='Search for a Job' icon='magnify' />
<View style={styles.filterContainer}>
<AppPicker
color='white'
selectedItem={category}
onSelectItem={item => handleCategoryChange(item)}
items={categories}
placeholder='Filter'
icon='apps' />
<AppPicker
color='white'
selectedItem={status}
onSelectItem={item => handleStatusChange(item)}
items={statuses}
placeholder='Status'
icon='apps' />
</View>
<FlatList
style={styles.list}
data={filteredJobs ? filteredJobs : jobs}
keyExtractor={job => job.id.toString()}
renderItem={({ item }) => (
<ListItem
company={item.company}
position={item.position}
status={item.status}
onPress={() => navigation.navigate('Details', { item })}
/>
)}
ItemSeparatorComponent={ListItemSeparator}
/>
</View>
);
Thanks in advance! Keep in mind jobs is fetched in a useEffect on loading the component.
It is because you should use a comparison, and not an attribuition.
const handleStatusChange = (item) => {
setFilteredJobs(
// FIX HERE, USE == INSTEAD OF =
jobs.filter( job => job.status == item.label )
)
setStatus(item)
}
#guilherme is right, it's a simple mistake, you assigned instead of comparing. Get in the habit of using === to compare strings.
Also the way to tackle these problems in the future: console.log before and after the thing you are doing that isn't working. It would have jumped out at you pretty quick if you had. Use JSON.stringify for logging of objects if you are getting [Object object] in the logging output.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
I have the following code,
I need to use a variable that is coming after a hook is set, how to use that in my render?
const LoanDetailCard = () => {
const loan = first(loanData?.loans); //var set from hook
let interestRateOutter;
let transactionalBalanceOutter;
if (loan) {
//set the outer variables with loanData???
const {interestRate, transactionalBalance} = loan
interestRateOutter = interestRate
transactionalBalanceOutter = transactionalBalance
}
return (
<View style={styles.container}> ...
//now use interesRateOutter ??
OR
is the best approach just to check on the render elements?
<Text style={styles.textWhite} accessibilityLabel="Interest rate">
{loan ? loan.interestRate : ""}
</Text>
You don't need any temporary variables in your render loop. Instead I would write it simply like:
const LoanDetailCard = (loanData) => {
const { interestRate, transactionalBalance } = first(loanData);
return <View style={styles.container}>
<Text style={styles.textWhite} accessibilityLabel="Interest rate">
{interestRate ?? ""}
</Text>
</View>;
};
Hi I'm wondering why this code works,
Sorry for the sintax errors, this is an Example. My question is why memberIpAssignments is taking ip value?. I don't get it if I'm no passing ip into setMemberIpAssignments(arr =>[...arr]) but still takes that's value and updating the state.
setMemberIpAssignments(arr =>[...arr]), this state shouldn't change at all, because I'm no giving ip value. But it does change taking ip value.
if someone can explain to me I'll be grateful.
I'm new at react-native
export const zeroTierNetworkMembersUpdateScreen = ({ route }) => {
const { ipAssignments } = ["192.168.0.1","192.168.1.1"];
const [memberIpAssignments, setMemberIpAssignments] =(ipAssignments);
return (
<View style={styles.viewSet}>
{memberIpAssignments.map((eachIpAssignments, index) => {
return (
<Input
key={index}
placeholder={"ipAssignments"}
keyboardType={"default"}
value={eachIpAssignments}
onChangeText={(value) => {
var ip = ipAssignments;
ip[index] = value;
setMemberIpAssignments(arr =>[...arr]);
}}
/>
);
})}
</View>
);
};
I think I've confirmed my suspicions that you are in fact mutating an object reference that you've stored in local component state.
export const zeroTierNetworkMembersUpdateScreen = ({ route }) => {
// (1) ipAssignments array reference
const ipAssignments = ["192.168.0.1", "192.168.1.1"];
// (2) memberIpAssignments references ipAssignments
const [memberIpAssignments, setMemberIpAssignments] = useState(ipAssignments);
return (
<View style={styles.viewSet}>
{memberIpAssignments.map((eachIpAssignments, index) => {
return (
<Input
key={index}
placeholder={"ipAssignments"}
keyboardType={"default"}
value={eachIpAssignments} // (3) value from memberIpAssignments
onChangeText={(value) => {
// (4) ip references ipAssignments & memberIpAssignments
var ip = ipAssignments;
// (5) element mutation!!
ip[index] = value;
// (6) state update to trigger rerender
setMemberIpAssignments(arr => [...arr]);
}}
/>
);
})}
</View>
);
};
As far as I can tell the mutation happens exactly once since initially everything is reference the original ipAssignments array. Upon updating state though, arr => [...arr] is returning a new array reference for memberIpAssignments the references back to ipAssignments is broken.
You should really be using a functional state update to "edit" the ip entry any way. Consider the following:
export default function App() {
const ipAssignments = ['192.168.0.1', '192.168.1.1'];
const [memberIpAssignments, setMemberIpAssignments] = React.useState(
ipAssignments
);
return (
<View>
{memberIpAssignments.map((eachIpAssignments, index) => {
return (
<TextInput
key={index}
placeholder={'ipAssignments'}
keyboardType={'default'}
value={eachIpAssignments}
onChangeText={(value) => {
setMemberIpAssignments((arr) =>
arr.map((el, i) => (i === index ? value : el))
);
}}
/>
);
})}
</View>
);
}
Expo Snack
So I'm building a DnD app for character sheet management, where it displays the skills and whatnot.
In it I have a class(AbiltityClass) that stores a map of other classes(SkillClass) inside of a variable(_aSkills).
export default class AbilityClass {
constructor(name, aVal, aMod) {
this._abilityName = name; // string
this._abilityVal = aVal; // int
this._abilityMod = aMod; // int
this._aSkills = new Map(); // map of SkillClass objects
}
}
export default class SkillClass {
constructor(name, prof, mod, bonus){
this._nameSkill = name; // string
this._profSkill = prof; // bool
this._modBonus = bonus; // int
this._modSkill = this.evalMod(mod); // int
}
evalMod(mod) {
return mod + this._modBonus;
}
}
The _aSkills variable was originally an Array of SkillClasses, but due to ease-of-accessing, I decided that a Map would be better.
I have a process that will basically iterate through each element in _aSkills and create components out of them through the skillFactory and buildSkill functions which should be placed in the brackets where the skillFactory function is called.
const AbilityContainer = (props) => {
const buildSkill = (value, key) => {
return(
<SkillSet
key={Math.random()}
skillName={key}
prof={value._profSkill}
skillVal={value._modSkill}
setProf={()=>{}}
/>
);
}
const skillFactory = () => {
return(props.ability._aSkills.forEach(buildSkill));
}
return(
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.header_text}>
{props.ability._abilityName}
</Text>
</View>
<View style={styles.statBox}>
<View style={styles.ability}>
<StackedHex isAbility={true} lowerVal={props.ability._abilityVal}/>
</View>
<View style={styles.skills}>
{skillFactory()} // =================> The function call in question
</View>
</View>
</View>
);
};
My issue is that either skillFactory or buildSkill is not returning the SkillSet component.
I've used some print logs and I've verified that buildSkill is receiving the correct data, but something isn't working. I don't get any errors or warnings, and the place where the SkillSet components should be is just empty.
As I said, I changed the _aSkills variable from an Array to a Map recently, and it was working as an Array. This is the format of my previous code:
const AbilityContainer = (props) => {
const buildSkill = (skill) => {
return(
<SkillSet
key = {Math.random()}
prof={skill._profSkill}
setProf={skill._profFunc}
skillName={skill._nameSkill}
skillVal={skill._modSkill}
/>
);
}
const skillFactory = () => {
return (props.ability._aSkills.map(x => buildSkill(x)));
}
return(
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.header_text}>
{props.ability._abilityName}
</Text>
</View>
<View style={styles.statBox}>
<View style={styles.ability}>
<StackedHex isAbility={true} lowerVal={props.ability._abilityVal}/>
</View>
<View style={styles.skills}>
<SkillSet
prof={props.ability._saveProf}
setProf={props.ability._saveProfFunc}
skillName={'Save'}
skillVal={props.ability._saveMod}
/>
{skillFactory()}
</View>
</View>
</View>
);
};
I'm using Android Studio for this build and I'm still relatively new to react-native. I triple-checked all my other components and their stylesheets to make sure they weren't interfering, so I know it has something to do with the AbilityContainer.
I've read up on the forEach function for Maps and as far as I can tell, I'm doing it right, but I feel like there is something that forEach is doing that I don't understand in terms of how it returns things.
Does anyone have any idea what the issue could be?
Thanks in advance.
forEach does something for each element, but doesn't return anything (even if you return in the callback, that's just returning from the cb, not the equivalent of returning from a .map) which is why that version isn't working. If you convert it back to an array ([...props.ability._aSkills].map...) this will give you an array of arrays ([key, value]) from your Map. You could also use a for..of loop over _aSkills.entries().
So now knowing that forEach doesn't return anything, I just added a variable and gave my buildSkill function something to return to.
Here's what I did:
const AbilityContainer = (props) => {
let skills = new Array();
const buildSkill = (value, key) => {
skills.push(<SkillSet
key={Math.random()}
skillName={key}
prof={value._profSkill}
skillVal={value._modSkill}
setProf={()=>{}}
/>);
}
const skillFactory = () => {
props.ability._aSkills.forEach(buildSkill);
return(skills);
}
...
};
I have list of products that are being generated each product has a checkbox associated with it. Right now all of my checkboxes are sharing the same state so when you check one, all of them end up being checked. The product data including the checkbox are in a loop , how can I generate checkboxes with different states? I am using React Native and the checkbox component
is using react-native-elements
<View>
{products.map((value, key) => {
return (
<View
key={value.serialNumber + key}
style={localStyles.productCheckbox}
>
<Text key={value._id + key} style={localStyles.modalText}>
{value.pModel}
</Text>
<Text
key={value.pModel + key}
style={localStyles.modalText}
>
{value.serialNumber}
</Text>
<Text
key={value.pVersion + key}
style={localStyles.modalText}
>
{value.versionNumber}
</Text>
<CheckBox
checked={this.state.checked}
onPress={() => {
this.setState({ checked: !this.state.checked });
}}
key={value._id}
/>
</View>
Instead of using a bool value, use an array that will store the ids of all the checked items.
Like this:
this.state = {
....
checked: [],
....
}
Now for each checkbox check whether its id exists inside array of not:
<CheckBox
checked={this.state.checked.includes(value._id)}
Update the state array inside onPress event handler:
onPress={(e) => this.handleCheckbox(value._id, e)}
handleCheckbox(id, e) {
// check whether item is checked or unchecked on the basis of add and remove
if (/* checked */) {
this.setState((prevstate) => ({
checked: [...prevState.checked, id]
}))
} else {
this.setState((prevstate) => ({
checked: prevState.checked.filter(el => el != id)
}))
}
}
You need to make a dictionary in your state that uses the unique id of the checkbox as a key, and the value will be the checked state.
// setting up your state in the ctor
this.state = { checkboxValues : {} }
<View>
{products.map((value, key) => {
const uniqueKey = value.serialNumber + key; // Unique identifier could probably be just the serial number i presume..
return (
<View
key={uniqueKey}
style={localStyles.productCheckbox}
>
<Text key={value._id + key} style={localStyles.modalText}>
{value.pModel}
</Text>
<Text
key={value.pModel + key}
style={localStyles.modalText}
>
{value.serialNumber}
</Text>
<Text
key={value.pVersion + key}
style={localStyles.modalText}
>
{value.versionNumber}
</Text>
<CheckBox
checked={this.state.checkboxValues[uniqueKey].checked} // read from the state dictionary based on the id
onPress={() => {
// assign a new checked state based on the unique id. You'll also need to do some error/undefined checking here.
const newCheckboxValues = this.state.checkboxValues;
newCheckboxValues[uniqueKey].checked = !newCheckboxValues[uniqueKey].checked
this.setState({ checkboxValues: newCheckboxValues });
}}
key={value._id}
/>
</View>
hi recently i also stumbled upon this issue, it seems easy to solve but i took a lot of time to solve it. The below code creates dynamic key-value pairs in state. if you console.log the state in your render you'll see (check0:true check1:true check2: false ....) Hope my solution helps someone.
<Switch
value={this.state[`check${key}`]}
onValueChange={value => this.setState({ [`check${key}`]: value })}
/>