Handle Sliders dynamically using slider from Material-UI - javascript

I want to generate a sliders dynamically according to user input, and don't know how to save values on change. Following is the code given of my implementation.
The problem is that I can't get value via event.target.value
// priceCities is an array of objects:
handlePrices(priceCities){
return priceCities.map( (cstate, index) => (
<li key={index} >{cstate.name} <Slider key={index} min={3} max={500} step={1} style={{height: 100}} axis="y"
defaultValue={5} id ={cstate.id} onChange={ this.handleSlider.bind(this,cstate.id )} value={this.state.values[cstate.id] } /> <span>{this.state.values[cstate.id]}</span> </li>
));
}
this.state = {
values: []
}
and onChange() method here:
handleSlider ( event,i ) {
// this.state.sliderValue[event.target.id] = event.target.value;
//console.log('handlerslider'+event.target.id+' '+event.target.value);
let values = [...this.state.values];
values[i] = event.target.value;
this.setState({ values });
}

Finally I found solution by defining the onChange method like this :
onChange={(event,value) => this.handleSlider(event, value,currState.id )}
and the code of handleSlider function :
handleSlider (event, value,id) {
let values = [...this.state.sliderValue];
values[id] = value;
this.setState({sliderValue: values });
console.log('handlerslider'+value+' '+id);
}

2020 Answer with Hooks
Something simple like this will work:
<Slider
onChange={(_, value) =>
setState(value)
}
step={1}
min={1}
max={50}
value={value}
valueLabelDisplay="auto"
/>

Related

React Native Updating an Array of JSON objects onChangeText, index undefined

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;
});
}

React Native useState onChangeText

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

React Controlled Form with Child /Parent component

I'm building a controlled form with dynamic fields.
The Parent component get data from a redux store and then set state with the values.
I don't want to make it with too much code lines so I turn the dynamic fields into a component.
States stay in the parent component and I use props to pass the handlechange function.
Parent :
function EditAbout(props) {
const [img, setImg] = useState("");
const [body, setBody] = useState(props.about.body);
const [instagram, setInstagram] = useState(props.about.links.instagram);
const [linkedin, setLinkedIn] = useState(props.about.links.linkedin);
const [press, setPress] = useState(props.about.press)
const handleSubmit = (e) => {
// Submit the change to redux
};
// set states with redux store
useEffect(() => {
setBody(props.about.body);
setInstagram(props.about.links.instagram);
setLinkedIn(props.about.links.linkedin);
setPress(props.about.press);
}, []);
const handleChangeChild = (e, index) => {
e.preventDefault();
let articles = press
const {value, name } = e.target
if (name === "title") {
articles[index].title = value;
} else {
articles[index].link = value;
}
setPress(articles)
console.log(articles[index])
}
return (
<Box>
<h1>CHANGE ABOUT ME</h1>
<Input
label="Image"
name="img"
type="file"
variant="outlined"
margin="normal"
onChange={(e) => setImg(e.target.files)}
/>
<Input
label="body"
value={body}
name="body"
onChange={(e) => setBody(e.target.value)}
variant="outlined"
multiline
rowsMax={12}
margin="normal"
/>
<Input
label="instagram"
value={instagram}
name="instagram"
variant="outlined"
margin="normal"
onChange={(e) => setInstagram(e.target.value)}
/>
<Input
label="Linkedin"
value={linkedin}
name="linkedin"
variant="outlined"
margin="normal"
onChange={(e) => setLinkedIn(e.target.value)}
/>
<Child press={press} onChange={handleChangeChild} />
{props.loading ? (
<CircularProgress color="black" />
) : (
<Button onClick={handleSubmit} variant="contained">
Send
</Button>
)}
</Box>
);
}
Child :
function Child(props) {
const { press, onChange } = props;
const inputsMarkup = () =>
press.map((article, index) => (
<div key={`press${index}`} style={{ display: "flex" }}>
<input
name="title"
value={press[index].title}
onChange={(e) => onChange(e, index)}
/>
<input
name="link"
value={press[index].link}
onChange={(e) => onChange(e, index)}
/>
<button>Delete</button>
</div>
));
return (
<div>
<h1>Press :</h1>
{inputsMarkup()}
</div>
);
}
Everything is fine when I'm typing in the Parent inputs. But when I'm using Child fields state update for one character but come back at its previous state right after.
It also doesn't display the character change. I can only see it in the console.
Thanks you in advance for your help
The problem is that you're mutating the state directly. When you create the articles variable (let articles = press) you don't actually create a copy and articles doesn't actually contain the value. It's only a reference to that value, which points to the object’s location in memory.
So when you update articles[index].title in your handleChangeChild function, you're actually changing the press state too. You might think that's fine, but without calling setPress() React will not be aware of the change. So, although the state value is changed, you won't see it because React won't re-render it.
You need to create a copy of the press array using .map() and create a copy of the updated array element. You can find the updated handleChangeChild() below:
const handleChangeChild = (e, index) => {
e.preventDefault();
const { value, name } = e.target;
setPress(
// .map() returns a new array
press.map((item, i) => {
// if the current item is not the one we need to update, just return it
if (i !== index) {
return item;
}
// create a new object by copying the item
const updatedItem = {
...item,
};
// we can safely update the properties now it won't affect the state
if (name === 'title') {
updatedItem.title = value;
} else {
updatedItem.link = value;
}
return updatedItem;
}),
);
};

How can I dynamically generate components?

I want to be able to generate a certain number of components (all similar) according to a value chosen in a select element.
I tried to generate this by creating an array of components whose size is the selected number. When I select a number it launch a handle function which changes the selected number in the creation.
handle = selected => {
this.state.number= selected.value;
this.forceUpdate();
};
render() {
return(
<Select onChange={this.handle} options = { ... }/>
{[...Array(this.state.number)].map(e => { return ( <TestComponent />
); })}
)
}
It loads the first component because the number is initialized to 1 but when I select another number 2,3,4,5,... it doesn't render the new components.
You can create a function like below:
makeComponentList=(num)=>{
let i=0,arr=[];
for(;i<num;i++){
arr.push(<TestComponent key={i} />)
}
return arr;
}
handle = selected => {
this.setState({number:selected.value});
}
render() {
return(
<React.Fragment>
<Select onChange={this.handle} options = { ... }/>
{this.makeComponentList(this.state.number)}
</React.Fragment>
);
}
You don't update the state so this.state.number value is always set to its initialized value:
handle = selected => {
this.setState({ number: selected.value }); // Will re-render the component
this.forceUpdate();
};
render() {
return(
<>
<Select onChange={this.handle} />
{[...Array(this.state.number)].map((e, i) => (
<TestComponent key={i} />
// ^ Add a unique key
))}
</>
)
}

value is NaN after parseInt() in react-native TextInput

I am trying to set an Int value from the state inside a <TextInput> by first turning it into string (<TextInput> can only receive a string) and then I want to be able to change the value and update the state value with the new <TextInput/> value. When I am trying to change the value inside the <TextInput/>
I get error:
undefined is not an object (evaluating 'this.state.keys.toString')
UPDATE:
I removed the this from this.keyInt and now I receive NaN on input update , the error is gone though
React-Native code:
class Counter extends Component {
constructor(props) {
super(props);
this.state = {
keys: 0,
id: this.props.id
};
}
updateKeysWithInputHandler = (e) => {
keyInt = parseInt(e.target.value);
console.log(`keyInt is: ${keyInt}`);
this.setState({
keys: keyInt
})
}
render() {
return (
<View style={styles.container}>
<TextInput
id={this.props.id}
style={styles.title}
keyboardType='numeric'
maxLength={2}
value={this.state.keys.toString()}
onChange={this.updateKeysWithInputHandler}
/>
</View>
);
}
Ok I fixed my problem thanks to this post and the help from Adam Azad which point me to my problem.
apparently react-native <TextInput/> dont use target and instead its use nativeEvent.text so I changed my code and it worked now.
Working code:
updateKeysWithInputHandler = (val) => {
keyInt = parseInt(val);
console.log(val);
this.setState({
keys: keyInt
})
}
render() {
return (
<View style={styles.container}>
<TextInput
id={this.props.id}
style={styles.title}
keyboardType='numeric'
maxLength={2}
value={this.state.keys.toString()}
onChange={(event) => this.updateKeysWithInputHandler(event.nativeEvent.text)}
/>
</View>
);
}
}
Why keys: this.keyInt?
Shouldn't it be keys: keyInt?
this.setState({
keys: this.keyInt
// ___^^^^^
})
Remove the this keyword because it changes the context from the current scope to Counter scope, where keyInt variable is not defined.
updateKeysWithInputHandler = (event) => {
if (isNaN(event.target.value) || event.target.value.toString().length===
0) {
event.target.value = 0;
}
this.setState({
keys: parseInt(event.target.value)
})
}

Categories