React Native checkbox of a map select - reactjs

I am mapping through an array of objects called abilities in React Native, that I retrieve from the backend and trying to select each one of them (abilities) assigned to each item. But selecting one of them, selects them all. How to effectively select a single checkbox in handleConfirm?
class Example extends Component {
constructor(props) {
super(props);
this.state = {
checked: false,
};
this.handleConfirm = this.handleConfirm.bind(this);
handleConfirm () {this.setState({ checked: true })}
render() {
const {
checked
} = this.state;
return (
<Container>
<Content contentContainerStyle={styles.container}>
<ListItem style={styles.listAllItems}>
{abilities.map((ability, index) =>
<Button
key={index}
iconLeft
style={styles.button}
>
<Text>{ability.name + '\n'}</Text>
<Right>
<CheckBox
checked={checked}
onPress={ this.handleConfirm()}/>
</Right>
</Button>
)}
</ListItem>
</Content>
</Container>

Below things you have missed in your code:
1] As you are mapping through array of object, you need to manage each checkbox state, which is missing in your code(i.e. you have used a single variable for maintaining check-boxes state which is not correct). You need a array for managing checked/unchecked status of each checkbox.
2] Also it has been observed that, you are handling only checked true condition. Actually you need to handle toggle(checked=>unchecked and unchecked=>checked).
I have made some modifications in your code for your issue and above specified changes as well:
class Example extends Component {
constructor(props) {
super(props);
this.state = {
checked: []
};
}
isItemChecked(abilityName) {
return this.state.checked.indexOf(abilityName) > -1
}
manageToggle = (evt, abilityName) => {
if (this.isItemChecked(abilityName)) {
this.setState({
checked: this.state.checked.filter(i => i !== abilityName)
})
} else {
this.setState({
checked: [...this.state.checked, abilityName]
})
}
}
render() {
return (
<Container>
<Content contentContainerStyle={styles.container}>
<ListItem style={styles.listAllItems}>
{abilities.map((ability, index) =>
<Button
key={index}
iconLeft
style={styles.button}
>
<Text>{ability.name + '\n'}</Text>
<Right>
<CheckBox
checked={this.isItemChecked(name)}
onPress={evt => this.manageToggle(evt, ability.name)}/>
</Right>
</Button>
)}
</ListItem>
</Content>
</Container>

do this for your onPress in your checkBox
onPress={()=>{ this.handleConfirm()}}

Related

Unmount or remove previously opened component before opening again

I have an inline edit component for the table. Whenever I click on the table cell it opens a small edit window to edit it.
Problem- if I am clicking on another cell it opened a new edit window and it will end up with multiple edit windows on the screen.
Need suggestions to remove/unmount the previous instance of the edit window. removing DOM element throwing an uncaught exception.
Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
Edits ---
Edit Component Code -
class TextEdit extends React.Component {
constructor(props) {
super(props);
this.state = { isInlineEditVisible: false };
}
render() {
return (
<span
style={{
marginLeft: '10px',
}}
>
<CellAction>
<EditIconButton
size={16}
onClick={() => {
this.setState({ isInlineEditVisible: true });
}}
/>
</CellAction>
{this.state.isInlineEditVisible && (
<InlineEdit
label={this.props.label}
value={this.props.param.dataPoint}
onSave={(value) => {
this.props.onSave(value, this.props.param);
}}
onCancel={() => {
this.setState({ isInlineEditVisible: false });
}}
/>
</span>
)}
</span>
);
}
}
I have written this component as a wrapper of InlineEdit component because I need it with each cell of multi-column table.
Here's the idea. Let's call the wrapper where you loop through cells as CellWrapper and Cell for the cell component.
const Cell = ({ cell, editCellId, setEditCellId }) => (
<div>
// Cell code
<EditIconButton
size={16}
onClick={() => {
setEditCellId({ editCellId: cell.id });
}}
/>
// EditWindow Code
{editCellId && editCellId === cell.id && <EditWindow />}
</div>
)
class CellWrapper extends Component{
state = {
editCellId: null
}
render(){
const { cells } = this.props;
return(
<div>{cells.map( cell =>
<Cell
cell={cell}
editCellId={this.state.editCellId}
setEditCellId={(editCellId) => this.setState({editCellId})}
/>
)
}</div>
)
}
}

Only check the clicked Switch, not working

I want to check only the clicked Switch, but if i click on a Switch, all Switches are toggle.
class LoadMeister extends Component {
constructor(props) {
super(props);
this.state = {
daten: [],
isLoading: true,
checked: false
};
this.userUpdate = this.userUpdate.bind(this);
}
userUpdate(checked) {
this.setState({ checked });
}
[...]
render() {
const ListeUser = this.state.daten.map(meister => (
<Col md={2} xs={3} sm={3} className="karten">
<h3
title={meister.SD_Vorname}
style={{
fontWeight: "bold",
textOverflow: "ellipsis",
overflow: "hidden",
whiteSpace: "nowrap"
}}
>
{meister.SD_Vorname} {meister.SD_Nachname}
</h3>
<hr />
<p />
<FaBeer size="2em" />
<FaBeer size="2em" style={{ float: "right" }} />
<div style={{ marginTop: "10px" }}>
<Switch
key={meister.SD_Emplid}
width={151}
onChange={this.userUpdate}
checked={this.state.checked}
offColor="#A5C9D7"
onColor="#00556A"
/>
</div>
</Col>
));
[Return()]
xxxxxxxxxxxxxxxxxxxxxxxxxx
So what i have to do, so that only the clicked Switch is Toggle ?
I use the react-switch Lib.
xxxxxxxxxxxxxxxxxxxxxxxxxx
Try something like:
userUpdate(checkedId) {
this.setState({ checkedId:id });
}
...rest of your code
<Switch
key={meister.SD_Emplid}
width={151}
onChange={()=>this.userUpdate(meister.SD_Emplid)}
checked={this.state.checkedId===meister.SD_Emplid}
offColor="#A5C9D7"
onColor="#00556A"
/>
Yes indeed, all switches are toggled because you have just one value in your state for all switches : this.state.checked.
You need to make one value for each switch, you can use an array or an object for this :
With an array
// constructor
this.state = {
checked: [] // all switches
};
// Update the checked state at the right index
userUpdate(checked, index) {
const newChecked = this.state.checked
newChecked[index] = checked
this.setState({ checked: newChecked });
}
// render
const ListeUser = this.state.daten.map((meister,index) => (
// More code
<Switch
key={meister.SD_Emplid}
width={151}
onChange={(checked) => this.userUpdate(checked, index)} // We need to know which index is clicked
checked={this.state.checked[index]}
offColor="#A5C9D7"
onColor="#00556A"
/>
)
With an object:
// constructor
this.state = {
checked: {} // all switches, keys will be the daten ids
};
// Update the checked state
userUpdate(checked, id) {
const newChecked = this.state.checked
newChecked[id] = checked
this.setState({ checked: newChecked });
}
// render
const ListeUser = this.state.daten.map(meister => (
// More code
<Switch
key={meister.SD_Emplid}
width={151}
onChange={(checked) => this.userUpdate(checked, meister.SD_Emplid)}
checked={this.state.checked[meister.SD_Emplid]}
offColor="#A5C9D7"
onColor="#00556A"
/>
)
The two solutions are very similar.
You use the checked state for all of your Switch components, which results in the behaviour you experience.
A solution would be to save checked in your meister objects:
userUpdate(index, checked) {
const { daten } = this.state;
daten[index].checked = checked;
this.setState({ daten });
}
/* inside your render */
this.state.daten.map((meister, index) => (
/* ... */
<Switch
{/* ... */}
checked={meister.checked}
onChange={this.userUpdate.bind(this, index)}
/>
));
I use the second argument of .bind to add index as the first argument to the userUpdate function.

How do I show an input field for each click on a button in react js?

I want to display an input field for every click on a button "Add Input Field". Now it is showing only one time when I click on button "Add Input Field". How Can I achieve this.
Here below I have created a codesandbox.
https://codesandbox.io/s/6lr8w994vn
To achieve this, you will have to store an array in your state containing the information to generate each field.
Then, in your add function, set your array to your deconstructed previous array, and your additional element:
class InputAdder extends React.Component {
constructor(props) {
super(props)
this.state = {
inputs: []
}
}
addInput = ev => {
this.setState(prev => ({ inputs: [...prev.inputs, 'Hi'] }))
}
render() {
return (
<div>
<button onClick={this.addInput}>Add input</button>
{this.state.inputs.map(node => <input type="text"/>)}
</div>
)
}
}
ReactDOM.render(<InputAdder/>, document.getElementById('root'))
input {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.5.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.5.2/umd/react-dom.production.min.js"></script>
<div id='root'>
You will then simply have to map over your array in your render function
One way is to store the number of inputs in the state, which will be incremented by one on every click. Then pass this value to props of your component and use map function to create the corresponding number of inputs.
To make this work, Change your state from this:
this.state = {
add: false,
addChild: false
};
to :
this.state = {
add: [],
addChild: []
};
and in addInputField function increase add array size every time it clicked, then use this.state.add to render your input fields. Same applies to Child.
Demo Component:
class Demo extends Component {
constructor() {
super();
this.state = {
add: [],
addChild: []
};
}
addInputField = event => {
const add = this.state.add;
const size = add.length + 1;
add.push(size);
this.setState({
add
});
event.preventDefault();
};
addChildInputField = event => {
const addChild = this.state.addChild;
const size = addChild.length + 1;
addChild.push(size);
this.setState({
addChild
});
event.preventDefault();
};
handleChange = event => {
this.setState({
[event.target.name]: event.target.value
});
};
render() {
return (
<div>
<Grid>
<Grid>
<Button onClick={this.addInputField} style={{ marginTop: 30 }}>
{" "}
<Icon>
<AddCircle />
</Icon>{" "}
Add Input Field
</Button>
{this.state.add.map(index => {
return (
<Grid>
<TextField
id="preference"
label="Preference"
name="points"
value={this.state.name}
onChange={this.handleChange}
margin="normal"
/>
<Button onClick={this.addChildInputField}> Add Child</Button>
</Grid>
);
})}
{this.state.addChild.map(index => {
return (
<Grid style={{ marginLeft: 50 }}>
<TextField
id="preference"
label="Child Preference"
name="points"
value={this.state.name}
onChange={this.handleChange}
margin="normal"
/>
</Grid>
);
})}
</Grid>
<Grid>
<Button
color="primary"
variant="contained"
type="submit"
style={{ margin: "0.2rem", marginTop: 30 }}
>
Save
</Button>
</Grid>
</Grid>
</div>
);
}
}
export default Demo;
Full code available here
Hope it helps.

React component opens all child states

I have a list of courses with student icon on them. When clicked on student icon it opens a modal and displays all assigned students on the course.
The problem I am having is when I click on one of the course to open the modal, it opens the modal for all the other courses. I know its to do with the state behaviour but I can't seem to figure out how best to tackle this problem.
Following is my code:
class CourseDetails extends React.Component {
constructor(props) {
super(props);
autobind(this);
this.state = { openStudentsAssignedToCourseModal: false };
}
closeStudentsAssignedToCourseModal() {
this.setState({ openStudentsAssignedToCourseModal: false });
}
render() {
const { store } = this.props;
const { openStudentsAssignedToCourseModal } = this.state;
return store.allCourses.map((course) => {
return (
<Container key={course.id}>
<p>{course.name}</p>
<UsersIcon
size={25}
onClick={() => {
if (course.listOfStudents.length > 0)
this.setState({
openStudentsAssignedToCourseModal: true
});
}}
/>
{openStudentsAssignedToCourseModal && (
<StudentsOnCourseModal
course={course}
isOpen
close={() => {
this.closeEmployeesAssignedModal();
}}
/>
)}
</Container>
);
});
}
}
Modal:
class StudentsOnCourseModal extends React.Component {
constructor() {
super();
autobind(this);
}
render() {
const { course, isOpen, close } = this.props;
const s = course.listOfStudents.length === 1 ? '' : 's';
return (
<Modal
isOpen={isOpen}
close={close}
width="large"
bgDismiss={false}
>
<ModalHeader>
<h2>Assigned students</h2>
</ModalHeader>
<ModalBody>
<p>
There {s === '' ? 'is' : 'are'}{' '}
<b>{course.listOfStudents.length}</b> student{s}{' '}
currently assigned to the course <b>{course.name}</b>.
</p>
<StudentsContainer>
{course.listOfStudents.map(student => (
<StudentItem key={student.id}>
<StudentCard
name={student.name}
link={`/student-profile/${
student.id
}/personaldetails`}
imageHref={
student._links.image
? student._links.image.href
: undefined
}
/>
</StudentItem>
))}
</StudentsContainer>
<OutlineButton
onClick={e => {
e.preventDefault();
close();
}}
>
Close
</OutlineButton>
</ModalBody>
</Modal>
);
}
}
I wasn't capturing the selected course so it was opening all of them. Fixed it by introducing a new state to capture the selected value and passed that into the modal.

Checkbox not Rendering or Changing Checked value when state is changing

I have to change the value of Checkbox to Checked from Unchecked and vise-versa.
For that I am changing state on button click.
and if 'checked' state value is 'true' then Checkedbox should be checked. But, It not.
I have used property 'checked' and assigned state's boolean value.
So, it should render when state going to be changed but it not changing.
Is there any other way to render Checkbox ?
This is a code sample how to do that
class App extends Component {
state = {
isChecked: false
};
render() {
return (
<View>
<CheckBox
value={this.state.isChecked}
onValueChange={() =>
this.setState({
isChecked: !this.state.isChecked
})
}
/>
</View>
);
}
}
If you are using redux-form then, this is your solution :
<Field name="AcceptTAndC" component={(props) => {
return (
<View>
<ListItem>
<CheckBox {...props.input} checked={props.input.value ? true : false} onPress={() => {
const val = !props.input.value;
props.input.onChange(val);
this.setState({ acceptTAndC: val });
}} />
<Text> I accept <Text onPress={() => this.refs.termsModal.open()}>this terms and conditions</Text></Text>
</ListItem>
</View>
)
}} validate={[acceptTerms]}/> // "acceptTerms" is validation rules that imported from other file, but you may not need it for this
You can check details from here code and output
as mentioned above by #Amila Dhulanjana, this function only changes the value of assigned state value, but it will not update the checkbox in HTML view until you assign the checked field.
class App extends Component {
state = {
isChecked: false,
};
render() {
return (
<View>
<CheckBox
value={this.state.isChecked}
checked={this.state.isChecked}
onValueChange={() =>
this.setState({
isChecked: !this.state.isChecked,
})
}
/>
</View>
);
}
}

Resources