Why am I not able to check/uncheck checkboxes? - reactjs

I am not able to check/uncheck checkboxes, but their checkmark value is being populated by the api and either checking them or leaving them unchecked. But for some reason I am not able to check/uncheck.
Tried putting them into different components and using onClick/onChange. Nothing works.
Parent component - PublishPage:
<div id="all-roles">
{this.props.listOfRoles ? (
<ListOfRoles checkedIDs={this.state.roleIDs} roles={this.props.listOfRoles} /> ) : null}
</div>
List of Roles
export default class ListOfRoles extends Component {
state = {
checkedIDs : []
}
static getDerivedStateFromProps(props) {
let checkedIDs = null;
if (props.checkedIDs) {
checkedIDs = props.checkedIDs
}
return { checkedIDs: checkedIDs };
}
render() {
const roles = this.props.roles[0].roles;
console.log(this.state);
return (
<div id="assign-to-role">
{roles.map(role => {
return (
<div>
<RoleCheckbox roleName={role.roleName} roleID={role.roleID} checkedArray={this.state.checkedIDs} />
</div>
);
})}
</div>
);
}
}
RoleCheckbox
export default class RoleCheckbox extends Component {
constructor(props) {
super(props);
this.state = {
checkboxDefault: true
}
this.handleCheckbox = this.handleCheckbox.bind(this);
}
static getDerivedStateFromProps(props) {
let checked = true;
let roleID = props.roleID;
let checkedArray = props.checkedArray;
if (checkedArray.indexOf(roleID) !== -1) {
checked = true
} else {
checked = false;
}
return { checkboxDefault: checked };
}
handleCheckbox(e) {
this.setState({
[e.target.name]: !this.state[e.target.name]
})
}
render() {
return (
<div>
<label> {this.props.roleName}
<input
type="checkbox"
name="checkboxDefault"
onChange={this.handleCheckbox}
checked={this.state.checkboxDefault} />
</label>
</div>
)
}
}
I want to be able to check/uncheck and also I want to be able to load them as checked if the api says they should be checked.

Actually what's happening here is that you are injecting the HTML with the boolean value from this.state.checkboxDefault which is initialized default to true inside RoleCheckbox component and the function that handles the change is not effecting the state. Change the state and it will reflect on the UI as well
handleCheckbox(e) {
this.setState({
checkboxDefault: !this.state.checkboxDefault
})
}

Related

React - dynamically create select all checkboxes

There has been a number of questions on React checkboxes. This answer is pretty good, and it helps to modularize the idea of select-all checkboxes in React.
Now, I have a problem. I have a dictionary like this:
{
regionOne: {
subareaOne,
subareaTwo,
subareaThree,
subareaFour
},
regionTwo: {
subareaFive,
subareaSix
}
}
Here, each region is mapped to an arbitrary number of sub areas, which I do not know beforehand.
I want to create checkboxes such that each region and each subarea has a checkbox, and each region's checkbox acts as a select-all/de-select all for all the subareas it is mapped to. That is, something like this:
So, when you click on the checkbox for regionOne, the checkboxes for subareaOne, subareaTwo, subareaThree and subareaFour should all be checked as well, but not those in regionTwo.
I think I can adapt this answer, but its getInitialState function assumes that you know how many children checkboxes there are.
Any idea on how to do this in an elegant method? I am now considering initialising the checkboxes dynamically using mapping, but I am not sure...VanillaJS would have been much simpler >.<
I actually went ahead and implemented it anyway. There are two components involved, a parent CheckboxGroup component, and a child StatelessCheckbox component.
Here is the parent component:
import React from 'react';
import { StatelessCheckbox } from './StatelessCheckbox';
export class CheckboxGroup extends React.Component {
constructor(props) {
super(props);
this.state = {
checked: false,
boxes: {},
disabled: false
};
}
componentDidMount() {
const { boxes, boxId, disabled } = this.props;
let boxesState = {};
boxes.map(box => {
boxesState[box[boxId]] = false;
});
this.setState({ checked: false, boxes: boxesState, disabled: disabled });
}
handleSelectAll(event) {
const isChecked = event.target.checked;
let boxesState = {};
Object.keys(this.state.boxes).map(box => {
boxesState[box] = isChecked;
});
this.setState({ checked: isChecked, boxes: boxesState });
}
handleSelect(event) {
const isChecked = event.target.checked;
const boxId = event.target.value;
let newBoxes = {};
Object.assign(newBoxes, this.state.boxes);
newBoxes[boxId] = isChecked;
// Check parent checkbox if all children boxes are checked
const checkedBoxes = Object.keys(newBoxes).filter((box) => {
return newBoxes[box] === true;
});
const parentIsChecked = (checkedBoxes.length === Object.keys(newBoxes).length);
this.setState({ checked: parentIsChecked, boxes: newBoxes });
}
render() {
const {
passDataToParent=(() => { return false; }),
groupClassName='',
headClassName='',
headName='',
headBoxClass='',
headLabelClass='',
headLabelText='',
bodyClassName='',
bodyName='',
bodyBoxClass='',
bodyLabelClass='',
boxes,
boxId,
boxLabel
} = this.props;
return (
<div className="row">
<div className={ groupClassName }>
<div className={ headClassName }>
<StatelessCheckbox name={ headName } className={ headBoxClass }
labelClass={ headLabelClass } labelText={ headLabelText }
checked={ this.state.checked } value={ headName }
passedOnChange={ (e) => { this.handleSelectAll(e); } } />
</div>
<div className={`row ${ bodyClassName }`}>
{ boxes.map(box => (
<div key={ box[boxId] }>
<StatelessCheckbox name={ bodyName } className={ bodyBoxClass }
labelClass={ bodyLabelClass } labelText={ box[boxLabel] }
checked={ this.state.boxes[box[boxId]] } value={ box[boxId] }
passedOnChange={ (e) => { this.handleSelect(e); } } />
</div>
))}
</div>
</div>
</div>
);
}
}
and here is the child component:
import React from 'react';
/**
* Implements a React checkbox as a stateless component.
*/
export class StatelessCheckbox extends React.Component {
constructor(props) {
super(props);
}
render() {
const {
passedOnChange=(() => { return false; }),
className='',
name='',
labelClass='',
labelText='',
value='',
checked=false,
} = this.props;
return (
<label className={`for-checkbox ${ className }`} tabIndex="0">
<input onChange={(e) => passedOnChange(e) }
checked={ checked } type="checkbox"
name={ name } value={ value } />
<span className={`label ${ labelClass }`}>{ labelText }</span>
</label>
);
}
}
Things to note:
the child component is a stateless component in this case, used purely for rendering
the parent component (CheckboxGroup) maintains the state for all the child components as well so there is still a single source of truth as per React philosophy
boxes in properties is a list of the children checkboxes in each parent CheckboxGroup, and boxes in the parent state holds the state for each child checkbox
Something like that?
onSelect(id) {
let allElms = Object.assign({}, retriveElmentsSomehow());
let elm = findTheElm(id, allElms);
updateSelected(elm, !elm.selected);
// Then update state with new allElms
}
updateSelected(elm, selectedOrNot) {
elm.selected = selectedOrNot;
if (elm.children) {
elm.children.foreach(c => onSelect(c, selectedOrNot));
}
}
Where elements are in state or redux store.

How to show validation message on <TagsInput> react premade component on unique value

I have an input tag component from react-tagsinput as follows:
const onTagChange = (tags) => {
const noDuplicateTags = tags.filter((v, i) => tags.indexOf(v) === i);
const duplicateEntered = tags.length !== noDuplicateTags.length;
if (duplicateEntered) {
onTagChange(tags);
console.log('duplicate');
}
onTagChange(noDuplicateTags);
};
function TagContainer({
tags,
}) {
return (
<div>
<Header>Meta:</Header>
<TagsInput value={tags} onChange={onTagChange} />
</div>
);
}
TagContainer.propTypes = {
tags: PropTypes.arrayOf(PropTypes.string),
};
TagContainer.defaultProps = {
tags: [],
};
export default TagContainer;
and the implementation on the onTagChange method which is passed as a prop to the <TagContainer> component in another component.
export class Modal extends React.Component {
...
...
onTagChange = (tags) => {
this.props.onTagChange(tags);
}
...
...
render() {
return(
<TagContainer
tags={tags}
onTagChange={this.onTagChange}
/>
);
}
}
Problem: onlyUnique prop in the <TagsInput> component is set to true to avoid duplicate entries. But I need to display an error message saying "duplicate values" as soon as user enters a duplicate value. How can this be done especially on the third party component.
I think you're going to have to handle dealing with duplicates in your component because you are getting no feedback from <TagInput /> component.
At a higher level, I would do something like this
class Example extends React.Component {
constructor() {
super();
this.state = {
showDuplicateError: false
};
}
handleTagChange(tags) {
const uniqueTags = removeDuplicates(tags);
const duplicateEntered = tags.length !== uniqueTags.length;
if (duplicateEntered) {
this.showDuplicateError();
}
// add unique tags regardless, as multiple tags could've been entered
const { onTagChange } = this.props;
onTagChange(uniqueTags);
}
showDuplicateError() {
this.setState({
showDuplicateError: true
});
}
render() {
const { showDuplicateError } = this.state;
const { tags } = this.props;
return (
<React.Fragment>
{ showDuplicateError && <div>Duplicate entered</div>}
<TagsInput value={ tags } onTagChange={ this.handleTagChange } />
</React.Fragment>
);
}
}

How to set the properties of a button child elements when it is clicked in react native

I have the following code which is available in all elements of a List and i will like to toggle the Icon active property when the button is clicked preferable using someFunction() method. I need help
import React from 'react';
export class QuestionsHelpers
{
username = '';
constructor(username)
{
this.username = username;
}
static renderQuestionContent(questionData, props, stateHandler)
{
/...///code
let status = questionData.user_liked === "1";
<Button transparent onPress={()=>someFunction()}>
<Icon active={status} name="thumbs-up"/>
<Text>Vote</Text>
}
}
below is where i call the method renderQuestion
questionList(props)
{
return this.state.data.map(function (questionData, index)
{
return QuestionsHelpers.renderQuestionContent(questionData, props, null)
}
);
}
and then the whole thing is inside the render method
render()
{
let data = <View/>;
if(this.state.data !== [])
{
data = this.questionList(this.props, null);
}
let spinner = this.state.loading === true? (<Spinner color='#FF7F00'/>) : (<Button title='Load more' onPress={()=>this.loadInitialState().done()}/>);
return (
<Content>
{data}
{spinner}
</Content>
);
}
You can use state for the button somewhere. For example:
class Doge extends React.Component {
state = {
status: false,
}
render() {
return (
<Button transparent={() => this.setState({ status: !this.state.status})>
<Icon active={this.state.status} name="thumbs-up" />
<Text>Vote</Text>
</Button>
);
}
}

How can I make each child of component has a state? react redux

In my project, there are HomeIndexView and table component. So, when a user logs in to his account, in HomeIndexView, it shows all tables in the database. What I want to do is that make each table have a state so that it changes color of depends on its state(depends on child's state)... How can I do this?
My table component has a state like below.
const initialState = {
allTables: [],
showForm: false,
fetching: true,
formErrors: null,
};
EDIT ---1
HomeIndexView
class HomeIndexView extends React.Component {
componentDidMount() {
setDocumentTitle('Table_show');
}
componentWillunmount() {
this.props.dispatch(Actions.reset());
}
_renderAllTables() {
const { fetching } = this.props;
let content = false;
if(!fetching) {
content = (
<div className="tables-wrapper">
{::this._renderTables(this.props.tables.allTables)}
</div>
);
}
return (
<section>
<header className="view-header">
<h3>All Tables</h3>
</header>
{content}
</section>
);
}
_renderTables(tables) {
return tables.map((table) => {
return <Table
key={table.id}
dispatch={this.props.dispatch}
{...table} />;
});
}
render() {
return (
<div className="view-container tables index">
{::this._renderAllTables()}
</div>
);
}
}
EDIT--2
_handleClick () {
const { dispatch } = this.props;
const data = {
table_id: this.props.id,
};
if (this.props.current_order == null) {
dispatch(Actions.create(data));
Object.assign({}, this.state, {
tableBusy: true
});
}
else{
this.props.dispatch(push(`/orders/${this.props.current_order}`));
}
}
The state you shared above is part of the global state (where tableReducer use) not the table's component state, so what you need is to initialize component state in Table React component, so that you can check some values to render css differently something like this:
import React from "react";
class TableComponent extends React.Component {
componentWillMount() {
this.setInitialState();
}
setInitialState() {
this.setState({ isWhatever: false });
}
render() {
return (
<div>
<h1 classname={this.state.isWhatever ? 'css-class' : 'another-class'}>
{this.props.id}
</h1>
</div>
);
}
}

Create a custom radio button using React JS

I'm trying to create a custom radio button. The issue that i'm facing is that i'm unable to uncheck the radio button when another radio button is clicked. Currently it behaves like a checkbox.
import {React, ReactDOM} from '../../shared/lib/react';
export default class RadioButton extends React.Component {
constructor(props) {
super(props);
this.state = {
checkedRadioBtn: false
};
this.toggleRadioBtn = this.toggleRadioBtn.bind(this);
};
toggleRadioBtn(){
this.setState({checkedRadioBtn: !this.state.checkedRadioBtn});
};
render() {
return (
<div className="radio-btn-group">
<div onClick={this.toggleRadioBtn} className={this.state.checkedRadioBtn ? "radiobtn checked" : "radiobtn unchecked"} data-value={this.props.value}></div>
<label>{this.props.text}</label>
</div>
);
}
};
You need to have container for group of radio buttons. That container will maintain the state for all the radio buttons and handle check/uncheck for each option. Here is the sample code for that,
import React from 'react'
import ReactDOM from 'react-dom'
class RadioBtn extends React.Component{
constructor(props) {
super(props);
}
handleClick(){
this.props.handler(this.props.index);
}
render() {
return (
<div className="radio-btn-group" onClick={this.handleClick.bind(this)}>
<div className={this.props.isChecked ? "radiobtn checked" : "radiobtn unchecked"} data-value={this.props.value}></div>
<label>{this.props.text}</label>
</div>
);
}
}
class RadioGrp extends React.Component{
constructor() {
super();
this.state = {
selectedIndex: null,
selectedValue: null,
options: ["option 0","option 1","option 2","option 3"]
};
}
toggleRadioBtn(index){
this.setState({
selectedIndex: index,
selectedValue: this.state.options[index],
options: this.state.options
});
}
render() {
const { options } = this.state;
const allOptions = options.map((option, i) => {
return <RadioBtn key={i} isChecked={(this.state.selectedIndex == i)} text={option} value={option} index={i} handler={this.toggleRadioBtn.bind(this)} />
});
return (
<div>{allOptions}</div>
);
}
}
var app = document.getElementById('app');
ReactDOM.render(<RadioGrp />, app);
Since you're using a div for a custom checkbox that doesn't behave like a normal checkbox you should be checking value against the selected value.
toggleRadioBtn(e){
this.setState({checkedRadioBtn: e.target.value});
};
Another question that I have is that you are assuming a single checkbox here so I have to assume you have a calling component that returns multiple instances. If that is the case then you need to pass your onClick down so you can pass the value back up to the parent. Then pass the selected value back down.
This is an example that I have in my application.
var languges = this.props.languages;
var languageToSelect = this.state.selectedLanguage;
var handleChange = this.handleChange;
var languageRows = Object.keys(languges).map(function(key) {
var language = languges[key];
return <LanguageBlock
key={ key }
language={ language }
languageCode={ key }
checked={ languageToSelect === key }
handleChange={ handleChange }
/>;
});
In my use case I have multiple languages and onChange I pass the selected language back up then on rerender the selected language will be updated so the radio options will reflect the change.
handleChange: function handleChange(event) {
this.setState({ selectedLanguage: event.target.value });
},
The handle change just sets state for the new value. The language block itself is just a simple component so no need to make it a class/component.
const LanguageBlock = ({ checked, language, languageCode, handleChange }) => {
return (
<div className="truncification">
<input type="radio" name="lang" id={ 'lang_' + languageCode }
value={ languageCode } checked={ checked } onChange={ (evt) => { handleChange(evt); } } />
<label htmlFor={ 'lang_' + languageCode }>{ language }</label>
</div>
);
};

Resources