React set checked property on componentDidMount - reactjs

I have multipage form and I want to set checked fields if I go back, I figured out how to do it but it doesn't work as I expected.
I find the elements I need to check, assign the attribute but nothing happens on UI, checked property is there in Chrome devtools
Here is the code:
if (Object.keys(this.props.data).length > 0) {
const elements = this.formRef.current.elements;
const IDs = ['1', '2', '3', '4', '5', '6']
IDs.map(id => {
if (elements[id]) {
if (Array.from(elements[id]).length > 1) {
let radio = Array.from(elements[id]).find(el => el.value == this.props.data.subData[id])
let r = ReactDOM.findDOMNode(radio);
r.checked = true;
} else {
if (id === 1) {
this.setState({
dropdownValue: this.props.data.subData[id]
})
}
let r = ReactDOM.findDOMNode(elements[id]);
r.value = this.props.data.subData[id]
}
}
})
}
}
Here is a component:
<Radio
label='...'
desc='...'
id='...'
name='...'
options={[
{ id: '...', label: '...', value: '...', name: '...' },
{ id: '...', label: '...', value: '...', name: '...' }]}
/>
That returns this:
<div className='form-input-group'>
<label>{label} <span>{desc}</span></label>
{options.map((option, index) => (
<RadioItem
key={index}
option={option}
defaultValue={this.state.value}
onChange={this.handleChange}
/>
))}
</div>

Related

How to add a static object before dynamic data map in React?

I use react-native-element-dropdown package in my app.
I want to add dynamic data in the component. But first, I would like to add an empty dropdown option value like:
{label: '-- Please Select --', value: ""}
const dropdownData = () => {
if(userList){
return userList?.filter(user => user.id != null).map((user, index)=> {
return {label: user.username, value: user.id}
})
}
}
The dropdownData is called in component's data property:
<Dropdown data={ dropdownData() } />
How can I add the empty value before the userList map?
Append your hard-coded option to the array before returning:
const dropdownData = () => {
if (userList) {
return [
{ label: '-- Please Select --', value: "" },
...userList
.filter(user => user.id != null)
.map(user => ({ label: user.username, value: user.id }))
];
}
}
You can do the same in the JSX:
<Dropdown data={[ ...dropdownData(), { label: '-- Please Select --', value: "" } ]} />
try this:
<Dropdown data={[{label: '-- Please Select --', value: ""}, ...dropdownData()]} />

Check/uncheck all checkbox implementation

I am trying to accomplish a checkbox list with an All functionality. The "ALL" toggle works to turn all the checkboxs from the list on/off. But once i try to toggle off the "ALL" checkbox once i unselect another item from the list, it doesnt work as expected, as it does not toggle off the item i selected. I used logs to see the behavior, and once the "ALL" logic kicks in, it appears that the whole list gets rerender and triggers the onChange with the new values. Any help is appreciate it
interface ContainerProps {
name: string;
flag: boolean;
}
const initList: ContainerProps[] = [
{name: "ALL", flag: false},
{name: "First item", flag: false},
{name: "Second item", flag: false},
{name: "Third item", flag: false},
{name: "Fourth item", flag: false},
]
const PodCounter: React.FC = () => {
const [propsList, setList] = useState(initList)
const onClick = (value: string, check: boolean) => {
const tempList = [...propsList]
if (value === "ALL" && check === true) {
tempList.forEach(function(item){
item.flag = check
})
} if (value != "ALL" && tempList[0].flag === true && check === false){
tempList[0].flag = check
}else {
tempList.forEach(function(item){
if (item.name === value){
item.flag = check
}
})
}
console.log("tempList", tempList)
setList(tempList)
}
return (
<IonList>
{propsList.map((obj, index)=>(
<IonItem key={index}>
<IonLabel>{obj.name}</IonLabel>
<IonCheckbox slot="end" value={obj.name} checked={obj.flag} onIonChange={e => onClick(e.detail.value, e.detail.checked)}></IonCheckbox>
</IonItem>
))}
</IonList>
);
};
This is what the app/console looks like when i clicked the "ALL" checkbox
You may separate "check/uncheck all" logic from the rest of your components and keep your app more SOLID, if I may say so:
const { useState } = React,
{ render } = ReactDOM,
rootNode = document.getElementById('root')
const Checkbox = ({value, checked, label, onChange}) => {
return (
<label>
<input type="checkbox" {...{value, checked, onChange}} />
{label}
</label>
)
}
const CheckOptions = ({options}) => {
const allUnset = options.reduce((acc, {value}) =>
({...acc, [value]: false}), {})
const allSet = options.reduce((acc, {value}) =>
({...acc, [value]: true}), {})
const [checkedOptions, setCheckedOptions] = useState(allUnset)
const toggleOption = value =>
setCheckedOptions({...checkedOptions, [value]: !checkedOptions[value]})
const toggleAll = () => {
const isAnythingChecked = Object.values(checkedOptions).some(Boolean)
isAnythingChecked
? setCheckedOptions(allUnset)
: setCheckedOptions(allSet)
}
return !!options.length && (
<ul>
{
options.map(({value, label}) => (
<li>
<Checkbox
key={value}
checked={checkedOptions[value]}
onChange={() => toggleOption(value)}
{...{value, label}}
/>
</li>
))
}
<li>
<Checkbox
value="all"
checked={Object.values(checkedOptions).some(Boolean)}
onChange={toggleAll}
label="ALL"
/>
</li>
</ul>
)
}
const options = [{value: 'a', label: 'Option A'}, {value: 'b', label: 'Option B'}, {value: 'c', label: 'Option C'}]
render (
<CheckOptions options={options} />,
rootNode
)
ul {
list-style-type: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>

How can I load multi select dynamically? [react-select]

How can I load Multi-Select dynamically?
I used react-select to implement MultiSelect.
My Efforts
In componentDidMount(), I fetched an array, which I want to display/load in my multi-select; then a response is stored in state.
Now, I tried to get value from that state, but I didn't get that value.
My Code
state= {Category: []}
// that category contain this values
//0: {categoryid: "1", categoryname: "Select Category"}
//1: {categoryid: "2", categoryname: "Abc"}
componentDidMount() {
fetch("http://myURL//file.php", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({})
})
.then(response => response.json())
.then(responseJson => {
this.setState({ Category: responseJson });
// If server response message same as Data Matched
console.log(this.state.Category);
window.parent.location = window.parent.location.href;
})
.catch(error => {
console.error(error);
});
}
//this code is not working, display nothing
<Select
closeMenuOnSelect={false}
components={animatedComponents}
isMulti
>
{this.state.Category.map((e, key) => {
return (
<option key={key} value={e.categoryid}>
{e.categoryname}
</option>
);
})}
</Select>
Please help me with this problem
react-select has options props.
<Select
closeMenuOnSelect={false}
components={animatedComponents}
options={this.state.Category.map(e => ({ label: e.categoryname, value: e.categoryid}))}
isMulti
onChange={newValue => this.setState({ selected: newValue })}
/>
How can I select values of this multi-select based on another select
component?
You can store selected values for both selects in state and filter options based on selected value.
I added quick sample with 2 dependent selects - Hospital (can have few doctors) and Doctor (can work in few hospitals).
When you select some Doctor - Hospital selection is updated and vice-versa.
Preview this code
import React, { useState } from "react";
import { render } from "react-dom";
import Select from "react-select";
const data = {
doctors: [
{
id: 1,
name: "Andrew",
hospitals: [{ id: 1, title: "Test Hospital" }, { id: 2, title: "Test2" }]
},
{
id: 2,
name: "Another",
hospitals: [{ id: 1, title: "Test Hospital" }, { id: 3, title: "Test3" }]
}
],
hospitals: [
{ id: 1, title: "Test Hospital" },
{ id: 2, title: "Test2" },
{ id: 3, title: "Test3" }
]
};
function App() {
const [selectedDoctor, setSelectedDoctor] = useState(null);
const [selectedHospital, setSelectedHospital] = useState(null);
const hospitalOption = item => ({ value: item.id, label: item.title });
const hospitalOptions = () => {
if (selectedDoctor) {
return data.doctors
.filter(doctor => doctor.id === selectedDoctor.value)[0]
.hospitals.map(hospitalOption);
} else {
return data.hospitals.map(hospitalOption);
}
};
const doctorOption = item => ({
value: item.id,
label: `Doctor ${item.name}`
});
const doctorOptions = () => {
if (selectedHospital) {
return data.doctors
.filter(
doctor =>
doctor.hospitals.filter(
hospital => hospital.id === selectedHospital.value
).length
)
.map(doctorOption);
} else {
return data.doctors.map(doctorOption);
}
};
const reset = () => {
setSelectedDoctor(null);
setSelectedHospital(null);
};
return (
<div className="App">
<h3>React-Select multi select sample</h3>
<Select
id="hospital"
value={selectedHospital}
onChange={setSelectedHospital}
options={hospitalOptions()}
selectedDoctor={selectedDoctor}
/>
<Select
id="doctor"
value={selectedDoctor}
options={doctorOptions()}
onChange={setSelectedDoctor}
selectedHospital={selectedHospital}
/>
<pre selectedDoctor={selectedDoctor} selectedHospital={selectedHospital}>
Selected Doctor: {JSON.stringify(selectedDoctor || {}, null, 2)}
<br />
Available Doctors: {JSON.stringify(doctorOptions() || {}, null, 2)}
</pre>
<pre selectedDoctor={selectedDoctor} selectedHospital={selectedHospital}>
Selected Hospital: {JSON.stringify(selectedHospital || {}, null, 2)}
<br />
Available Hospitals: {JSON.stringify(hospitalOptions() || {}, null, 2)}
</pre>
<button onClick={reset}>Reset</button>
</div>
);
}
render(<App />, document.getElementById("root"));

Cannot set property 'indeterminate' of null in React

In my application, I need something like:
When a questions value is null then the checkbox should be shown as indeterminate, otherwise should be checked or not-checked.
But the problem is that when I update the questions, it shows me the error:
TypeError: Cannot set property 'indeterminate' of null
My questions object in state is like this:
questions: [{
id: 1,
title: 'First Question',
answers: [
{
id: 2,
title: 'Java',
value: ''
},
{
id: 3,
title: 'Python',
value: ''
},
{
id: 4,
title: '.NET',
value: true
}
]
}]
So it means that the third checkbox should be checked, and other two should be shown as indeterminate.
See picture below:
So when I click on the first one, it should become unchecked,and after clicking it again, its value should be true and should become checked. And their value will never be '' ever, except that it can be the first time.
Here's the question.jsx
import React, { Component } from 'react';
class Question extends Component {
state = {
questions: []
}
componentDidMount() {
const questions = [{
id: 1,
title: 'First Question',
answers: [
{
id: 2,
title: 'Java',
value: ''
},
{
id: 3,
title: 'Python',
value: ''
},
{
id: 4,
title: '.NET',
value: true
}
]
}, {
id: 2,
title: 'Second Question',
answers: [
{
id: 5,
title: 'MongoDB',
value: ''
},
{
id: 6,
title: 'MSSQL',
value: ''
},
{
id: 7,
title: 'MySQL',
value: ''
}
]
}, {
id: 3,
title: 'Third Question',
answers: [
{
id: 8,
title: 'ReactJs',
value: ''
},
{
id: 9,
title: 'Angular',
value: ''
},
{
id: 10,
title: 'VueJs',
value: ''
}
]
}]
this.setState({
questions
})
}
setIndeterminate = (elm, value) => {
if (value !== '') {
elm.checked = value;
elm.indeterminate = false;
}
else {
elm.checkbox = false;
elm.indeterminate = true;
}
}
handleOnChange = ({ currentTarget: checkbox }) => {
var questions = [...this.state.questions];
questions.map(p => {
p.answers.map(a => {
if (a.id == checkbox.id) {
a.value = (a.value === '') ? false : !a.value;
return;
}
})
})
this.setState({
questions
})
}
render() {
const { questions } = this.state
return (
<div>
{questions.map(question =>
<div key={question.id} className='question-wrapper'>
<div className="row">
<h6 className='text-left'>{question.title}</h6>
</div>
{question.answers.map((answer, i) =>
<div key={answer.id} className="form-group row">
<div className="form-check">
<input onChange={this.handleOnChange} ref={elm => this.setIndeterminate(elm, answer.value)} value={answer.value} className="form-check-input" type="checkbox" id={answer.id} name={answer.id} />
<label className="form-check-label" htmlFor={answer.id}>
{answer.title}
</label>
</div>
</div>
)}
</div>
)}
</div>
);
}
}
export default Question;
How is that possible of happening since as you can see I am already setting the value of intermediate to either true or false?
SOLUTION
I removed that setIndeterminate function, and did this inside ref in input element:
<input onChange={this.handleOnChange} ref={elm => {
if (elm) {
elm.checked = (answer.value !== '') ? answer.value : false;
elm.indeterminate = (answer.value === '') ? true : false;
}
}} value={answer.value} className="form-check-input" type="checkbox" id={answer.id} name={answer.id} />
I guess the problem whas that I needed to add that if (elm) to check that first.
I found this solution here (Thanks to ROBIN WIERUCH for this awesome article ) and works fine for me:
We want to extend the functionality of this checkbox for handling a tri state instead of a bi state. First, we need to transform our state from a boolean to an enum, because only this way we can create a tri state:
const CHECKBOX_STATES = {
Checked: 'Checked',
Indeterminate: 'Indeterminate',
Empty: 'Empty',
};
and now we can use it in pur component:
const Checkbox = ({ label, value, onChange }) => {
const checkboxRef = React.useRef();
React.useEffect(() => {
if (value === CHECKBOX_STATES.Checked) {
checkboxRef.current.checked = true;
checkboxRef.current.indeterminate = false;
} else if (value === CHECKBOX_STATES.Empty) {
checkboxRef.current.checked = false;
checkboxRef.current.indeterminate = false;
} else if (value === CHECKBOX_STATES.Indeterminate) {
checkboxRef.current.checked = false;
checkboxRef.current.indeterminate = true;
}
}, [value]);
return (
<label>
<input ref={checkboxRef} type="checkbox" onChange={onChange} />
{label}
</label>
);
};

ReactJS Render Table for each value on Array

I have a MultiSelect and a React Table..
the Select stores the values into value Array..
The way it is now i´m able to select ONE option and the table displays the data correctly. But, i´m looking to render a table for each selected option. How could i achieve something like this?
handleSelectChange (value) {
console.log('You\'ve selected:', value);
this.setState({ value: value }, () => this.fetchTable());
}
fetchTable() {
const url = 'http://localhost:8000/issues/from/';
const value = this.state.value;
const string = url+value;
fetch(string)
.then(function(response) {
return response.json();
})
.then((myJson) => this.setState({data: myJson.issues}));
}
componentDidMount() {
this.fetchData();
}
render() {
const filteredResult = this.state.boards.map(item => (
{
value: item.key,
label: item.name,
}
));
const filteredResult1 = this.state.data.map(item => (
{
name: item.fields,
id: item.id,
key: item.key,
}
));
return (
<div>
<Select
closeOnSelect={!stayOpen}
disabled={disabled}
multi
onChange={this.handleSelectChange}
options={filteredResult}
placeholder="Select Assignee(s)..."
removeSelected={this.state.removeSelected}
rtl={this.state.rtl}
simpleValue
value={value}
/>
<ResponseTable data={filteredResult1} />
</div>
);
}
}
How does your ResponseTable component look like? I guess you can just use the map function to loop and display the table rows. Sth like this:
const data = [{name: 'Test 1', id: 1, key: 'key_1'}, {name: 'Test 2', id: 2, key: 'key_2'}, {name: 'Test 3', id: 3, key: 'key_3'}];
_renderTableBody = () => {
return data.map((item) => (
return (
<TableRow>
<TableCell>item.name</TableCell>
<TableCell>item.id</TableCell>
<TableCell>item.key</TableCell>
</TableRow>
)
))
}
Then inside your render function, you can just replace this
<ResponseTable data={filteredResult1} />
into the code like this:
{this._renderTableHead()} // same method as _renderTableBody() to generate the table head title
{this._renderTableBody()}
Hope this can help!
Just keep some dummy key in state which as empty array initially. It will push the selected value of select option in to it. like below
constructor(props){
this.state = {
selectedValues: []
}
}
Alter your handleSelectChange like below. It needs to update the current selected value in this array
handleSelectChange (value) {
console.log('You\'ve selected:', value);
//this.setState({ value: value }, () => this.fetchTable());
let currentSelectedValue = this.state.selectedValues.filter(selected => selected == value)
//it will return a value if it is found otherwise empty array will be returned
if(currentSelectedValue.length == 0){
let updatedSelectedValue = this.state.selectedValues.push(value)
this.setState({ selectedValues: updatedSelectedValues }, () => this.fetchTable());
}
}
removeSelected (value) {
console.log('You\'ve selected:', value);
//this.setState({ value: value }, () => this.fetchTable());
let currentSelectedValue = this.state.selectedValues.filter(selected => selected !== value) //this will delete the removed option from array
this.setState({ selectedValues: currentSelectedValue }, () => this.fetchTable());
}
fetchTable() {
if( this.state.selectedValues.length > 0 ){
this.state.selectedValues.map((value)=>{
const url = 'http://localhost:8000/issues/from/';
const string = url+value;
fetch(string)
.then(function(response) {
return response.json();
})
.then((myJson) => this.setState({data: [...this.state.data, myJson.issues]})); //use spread operator to combine the result of each selectedValue
});
}
}
render() {
const filteredResult = this.state.boards.map(item => (
{
value: item.key,
label: item.name,
}
));
const filteredResult1 = this.state.data.map(item => (
{
name: item.fields,
id: item.id,
key: item.key,
}
));
return (
<div>
<Select
closeOnSelect={!stayOpen}
disabled={disabled}
multi
onChange={this.handleSelectChange}
options={filteredResult}
placeholder="Select Assignee(s)..."
removeSelected={this.state.removeSelected}
rtl={this.state.rtl}
simpleValue
value={value}
/>
this.state.data.map((item) => { // item here will hold the json object of { id: item.id, key: item.key, name: item.fields }
<ResponseTable data={item} />
})
</div>
);
}
}

Resources