React Array checkbox to check only one checkbox - reactjs

I have several checkbox groups on my Component of which only one of the should be selected. Each group has the same number of checkboxes (3 in the example), and the one selected is identified by the checked key inside the data list.
How can I handle this state?
class test extends Component {
constructor(props) {
super(props);
this.state = {};
const data = [
{ name: orange, checked: 2 },
{ name: apple, checked: 3 },
{ name: banana, checked: 1 }
];
}
render() {
return (
<>
{data.map(items => (
<tr>
<td>{items.name}</td>
<td>
<div>
<input type="checkbox" value="1" checked={true} />
</div>
</td>
<td>
<div>
<input type="checkbox" value="2" checked={false} />
</div>
</td>
<td>
<div>
<input type="checkbox" value="3" checked={false} />
</div>
</td>
</tr>
))}
</>
);
}
}

Try organizing your data-structure to look something like this:
data: [
{ name: "orange", boxes: [1, 2, 3], selected: null },
{ name: "apple", boxes: [1, 2, 3], selected: null },
{ name: "pineapple", boxes: [1, 2, 3], selected: null }
]
That gives us a group-name, an array of values to choose from and a selected value.
We'll be manipulating that data via our component-state.
Here's a codesandbox for reference: https://codesandbox.io/s/gallant-paper-b1d4z
Working code:
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [
{ name: "orange", boxes: [1, 2, 3], selected: null },
{ name: "apple", boxes: [1, 2, 3], selected: null },
{ name: "pineapple", boxes: [1, 2, 3], selected: null }
]
};
}
handleOnChange = e => {
const { data } = this.state;
const { name, value } = e.target;
const updatedData = data.map(group => {
if (group.name === name) {
return {
...group,
selected: group.selected === value ? null : value
};
} else {
return group;
}
});
this.setState({ data: updatedData }, () => console.log(this.state));
};
createInputGroups = () => {
const { data } = this.state;
const groups = data.map(group => {
return (
<div style={{ display: "flex" }}>
<div>{group.name}</div>
<div>
{group.boxes.map(box => {
return (
<input
onChange={this.handleOnChange}
type="checkbox"
name={group.name}
value={box}
checked={group.selected == box}
/>
);
})}
</div>
</div>
);
});
return groups;
};
render() {
return <div>{this.createInputGroups()}</div>;
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Pleases excuse my CSS :)

You can check at the checked option if the value of the checkbox is the same as the checked value of your item interface. Check this component as an example:
function CheckboxGroupRow({ name, checked, onUpdate }) {
return (
<tr className="CheckboxGroup">
<td>{name}</td>
<td>
<input
type="checkbox"
value={1}
checked={checked === 1}
onChange={e => onUpdate({ name, checked: +e.target.value })}
/>
</td>
<td>
<input
type="checkbox"
value={2}
checked={checked === 2}
onChange={e => onUpdate({ name, checked: +e.target.value })}
/>
</td>
<td>
<input
type="checkbox"
value={3}
checked={checked === 3}
onChange={e => onUpdate({ name, checked: +e.target.value })}
/>
</td>
</tr>
);
}
Each checkbox has a value, and it's checked only if the value of the checked variable matches to the one on the checkbox. For the onChange handle I have an onUpdate function that will be called with an updated data item whenever a user clicks a checkbox. Some logic upstream should handle the update.
Please take a look at this example build on CodeSandbox:
https://codesandbox.io/embed/checkboxgroup-cyv4p
I hope it helps

Related

How to add new property to an array of object through mapping in ReactJs

export default function App() {
const userData = [
{
id: 1,
name: "Gowtham",
followers: [
{ id: 11, name: "Anna" },
{ id: 12, name: "Theo" }
]
},
{
id: 2,
name: "Billy",
followers: [
{ id: 11, name: "Oliver" },
{ id: 12, name: "Emma" }
]
}
];
const [userToChat, setUserToChat] = useState("");
return (
<div className="App mt-5">
<h1>Message users followers</h1>
<select className="form-select" onChange={(e) => setUserToChat(e.target.value)}>
<option value="">Select</option>
<option value="Gowtham">Gowtham</option>
<option value="Billy">Billy</option>
</select>
{userData.map((user) => {
return (
{user.name === userToChat ? (
<div className="">
{user.followers.map((followers) => {
return (
<div className="d-flex">
<label className="col-form-label">{followers.name}</label>
<input type="text" name={followers.name} className="form-control-sm" />
</div>
);
})}
</div>
)
: ""
}
);
})}
</div>
);
}
Steps
First, I'm mapping through the userData object and checks the user name == chatToUser ("Gowtham").
If true, I map again to get the followers list of that selected user(Gowtham).
After I get the followers list(of Gowtham), I just want to add new property called message in followers object of userData and save the value that we get from the input field(Theo: "Hello, My man Theo") in the followers object in userData object.
Check this Images to see UI
![The UI Before selecting userToChat]
(https://i.stack.imgur.com/Q8Jny.png)
![The UI After selected User]
(https://i.stack.imgur.com/wz7JS.png)
example: the object should be like this after I enter values in input field.
{
id: 1,
name: "Gowtham",
followers: [
{ id: 11, name: "Anna", message: "Hi Anna"},
{ id: 12, name: "Theo", message: "Hello, My man Theo"}
]
}
I want to add new property called message in followers object of userData and save the value that we get from the input field(Theo: "Hello, My man Theo") in the followers object in userData object.
You'd need to manage the user data in state and then modify the state each time a message is updated.
Splitting your code into components can also help you more easily reason about the JSX.
Here's a code snippet demo:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.3/dist/css/bootstrap.min.css" />
<div id="root"></div><script src="https://cdn.jsdelivr.net/npm/react#18.2.0/umd/react.development.js"></script><script src="https://cdn.jsdelivr.net/npm/react-dom#18.2.0/umd/react-dom.development.js"></script><script src="https://cdn.jsdelivr.net/npm/#babel/standalone#7.20.15/babel.min.js"></script>
<script type="text/babel" data-type="module" data-presets="env,react">
// This Stack Overflow snippet demo uses UMD modules
const {StrictMode, useCallback, useState} = React;
const getInitialUserData = () => [{"id":1,"name":"Gowtham","followers":[{"id":11,"name":"Anna"},{"id":12,"name":"Theo"}]},{"id":2,"name":"Billy","followers":[{"id":11,"name":"Oliver"},{"id":12,"name":"Emma"}]}];
function Follower ({follower, updateFollowerMessage}) {
return (
<div className="d-flex">
<label className="col-form-label">{follower.name}</label>
<input
type="text"
name={follower.name}
className="form-control-sm"
onChange={ev => updateFollowerMessage(follower, ev.currentTarget.value)}
value={follower.message ?? ""}
/>
</div>
);
}
function App () {
const [users, setUsers] = useState(getInitialUserData);
const [activeUserIndex, setActiveUserIndex] = useState(-1);
const updateFollowerMessage = useCallback((
follower,
message,
) => setUsers(
users => users.map((u, i) => {
if (i !== activeUserIndex) return u;
const followers = u.followers.map(
f => f === follower ? {...f, message} : f
);
return {...u, followers};
})
), [activeUserIndex, setUsers]);
const user = users[activeUserIndex];
return (
<div className="App mt-5">
<h1>Message users followers</h1>
<select
className="form-select"
onChange={ev => setActiveUserIndex(Number(ev.currentTarget.value))}
>
<option value={-1}>Select</option>
{
users.map((user, index) => (
<option key={user.id} value={index}>{user.name}</option>
))
}
</select>
{
user ? (
<div className="">
{
user.followers.map(follower => (
<Follower
key={follower.id}
follower={follower}
updateFollowerMessage={updateFollowerMessage}
/>
))
}
</div>
) : null
}
</div>
);
}
const reactRoot = ReactDOM.createRoot(document.getElementById("root"));
reactRoot.render(
<StrictMode>
<App />
</StrictMode>
);
</script>

State in react is not rendering/displaying to the screen

I'm not able get what I'm doing wrong here. Could someone please help me out here. I'm unable to print the value to the screen. I'm trying to print the value from the state to the screen but I'm unable to.
Its not showing any error. I know this could be a silly mistake. Please help me out. Thanks
I'm not able get what I'm doing wrong here. Could someone please help me out here. I'm unable to print the value to the screen. i'm trying to print the value from the state to the screen but i'm unable to.
Its not showing any error.
import React, { Component } from "react";
import "./Screen.css";
export default class Screen extends Component {
constructor(props) {
super(props);
this.state = {
roles: [
{ id: 1, value: "Contracts manager", isChecked: false },
{ id: 2, value: "In-house counsel", isChecked: false },
{ id: 3, value: "IT administrator", isChecked: false },
{ id: 4, value: "Law clerk", isChecked: false },
{ id: 5, value: "Legal administrator", isChecked: false },
{ id: 6, value: "Legal operations manager", isChecked: false },
{ id: 7, value: "Legal secretary", isChecked: false },
{ id: 8, value: "Paralegal", isChecked: false },
{ id: 9, value: "Procurement manager", isChecked: false },
{ id: 10, value: "Solicitor", isChecked: false },
{ id: 11, value: "Other", isChecked: false },
],
aksha: "aksk",
};
console.log(this.state);
}
roleHandler = (event) => {
let roles = this.state.roles;
roles.forEach((role) => {
if (role.value === event.target.value)
role.isChecked = event.target.checked;
});
this.setState({ roles: roles });
};
render() {
const newRoles = this.state.roles.map((role) => {
return (
<div className="border-inp">
<label className="checkbox">
<span className="checkbox__input">
<input
type="checkbox"
name="checkbox"
key={this.state.roles.id}
onClick={this.roleHandler}
value={this.state.roles.value}
checked={this.state.roles.checked}
/>{" "}
{this.state.roles.value}
</span>
</label>
</div>
);
});
return (
<div>
<div className="Screenbg">
<div id="viewport" className="screenview">
<h3 className="wel">Welcome</h3>
<div>
<h5 className="role">What is your role?</h5>
{newRoles}
</div>
<div className="grid-container btndiv">
{/* <div className="grid-child purple">
<button className="btn">Back</button>
</div> */}
<div className="grid-child green">
{/* <Link to="/"> */}
<button className="btn">Next</button>
{/* </Link> */}
</div>
</div>
</div>
</div>
</div>
);
}
}
role must be used for rendering your array. You were using state to get it.
render() {
const newRoles = this.state.roles.map((role) => {
return (
<div className="border-inp">
<label className="checkbox">
<span className="checkbox__input">
<input
type="checkbox"
name="checkbox"
key={this.state.roles.id}
onClick={this.roleHandler}
value={this.state.roles.value}
checked={this.state.roles.checked}
/>
{role.value}
</span>
</label>
</div>

Does not render the component at the press of a button React Hooks

I have the next code.
import React, { useState, useEffect } from 'react';
import Opciones from './Opciones'
import './TableColumnas.scss';
const TableColumnas = ({data, distribuida}) => {
const addOption = (i, j) => {
data.tiempo_comida[i].columnas[j].push("Hello");
console.log(data);
}
const addColumn = (i, j) => {
data.tiempo_comida[i].columnas.push([]);
console.log(data);
}
const change = (e, i, j, k) => { //e: event
if(k){
data.tiempo_comida[i].columnas[j][k] = e.target.value;
}else{
data.tiempo_comida[i].columnas[j][0] = e.target.value;
}
}
return(
<>
<table className="content-table">
<tbody>
<>
{data.tiempo_comida.map((grupo_comidas,i)=>(
<>
<tr key={i}>
<p>{grupo_comidas.nombre_tiempo.label}</p>
</tr>
<tr>
{grupo_comidas.columnas.map((comidas, j)=>(
<>
<td key={j}>
{(comidas.length !== 0) ?
comidas.map((comida,k)=>(
<>
{console.log(comida)}
<div className="container" key={k}>
<div className="input">
<input type="text" placeholder="Text" defaultValue={comida} onChange={(e) => {change(e, i, j, k)}}/>
</div>
</div>
</>
))
:
<>
<div className="container">
<div className="input">
<input type="text" placeholder="Text" onChange={(e) => {change(e, i, j)}}/>
</div>
</div>
</>
}
<button onClick={()=>{addOption(i, j)}}>
+option
</button>
</td>
</>
))}
<button onClick={()=>{addColumn(i)}}>
+column
</button>
</tr>
</>
))}
</>
</tbody>
</table>
</>
)
}
export default TableColumnas;
And the data received in the prop are as follows
var tiempo_comida = [{
nombre_tiempo:{
name: "desayuno",
label: "Desayuno",
image: "desayuno",
color: "#EDC940"
},
columnas:[[],['jugo','cafe'],['tortillas','pan']]
},{
nombre_tiempo:{
name: "colacion1",
label: "ColaciĆ³n",
image: "colacion1",
color: "#54BDE9"
},
columnas:[['huevo','tocino','queso'],['jugo','cafe'],['tortillas','pan']]
},{
nombre_tiempo:{
name: "comida",
label: "Comida",
image: "comida",
color: "#72BEB2"
},
columnas:[['huevo','tocino','queso'],['jugo','cafe'],['tortillas','pan']]
},{
nombre_tiempo:{
name: "colacion2",
label: "ColaciĆ³n",
image: "colacion2",
color: "#4868AC"
},
columnas:[['huevo','tocino','queso'],['jugo','cafe'],['tortillas','pan']]
},{
nombre_tiempo:{
name: "cena",
label: "Cena",
image: "cena",
color: "#9563A3"
},
columnas:[['huevo','tocino','queso'],['jugo','cafe'],['tortillas','pan']]
},
]
If I press the buttons to add an option or a column and take out a data console.log gives me the values with the Hello added, but the screen does not increase.
The objective is that when pressing the button an element is added to the Array and therefore a new input is added
You cannot mutate props directly, you must update them in the parent.
addOption, addColumn and change functions should be in the parent and passed to this component as props.

Child component should render similar child component as sibling in parent component

I am new to react so bear any nontechnical words.
I have parent component that displays the table headers, now next is child component which has tables' td along with one td is add button when the user clicks on add button. The similar child component should be added as a sibling to previous child component and this process should go on.
Child Component:
class ChildComp extends React.Component{
state = {
Avalue: {value: ''},
Bvalue: {value: ''},
Cvalue: {value: ''},
Dvalue: {value: ''},
Evalue: {value: ''},
Fvalue: {value: ''},
Gvalue: {value: ''},
}
AddanotherSimilarChildcomp=(e)=>{
e.preventDefault();
const historyData = {
A: this.state.A.value,
B:this.state.B.value,
C: this.state.C.value,
D: this.state.D.value,
E: this.state.E.value,
F: this.state.F.value,
G: this.state.G.value,
}
console.log(historyData);
//and should render another similar ChildComp component below the one in which the current ChildComp is present
}
handleChange=(e)=>{
e.preventDefault();
const target = e.target;
const inputName = target.name;
const inputValue = target.value;
this.setState({
[inputName] : {
value: inputValue,
}
});
}
render(){
return(
<tbody id="first-table-row">
<tr>
<td data-th="A"><input type="text" minmax="40" name="A" value={this.state.a.value} onChange={this.handleChange} /></td>
<td data-th="B"><input type="date" minmax="40" name="B" value={this.state.B.value} onChange={this.handleChange} /></td>
<td data-th="C"><input type="text" minmax="225" name="C" value={this.state.C.value} onChange={this.handleChange} /></td>
<td data-th="D"><input type="text" minmax="40" name="D"value={this.state.D.value} onChange={this.handleChange} /></td>
<td data-th="E"><input type="text" minmax="40" name="E" value={this.state.E.value} onChange={this.handleChange} /></td>
<td data-th="F"><input type="text" minmax="40" name="F" value={this.state.F.value} onChange={this.handleChange} /></td>
<td data-th="G">
<div id="samerow">
<span>{this.props.somedata}</span>
<input type="text" minmax="40" name="G"value={this.state.G.value} onChange={this.handleChange} />
</div>
</td>
<td className="save" ><button id="save-btn" onClick={this.AddanotherSimilarChildcomp} type='button'>Add</button></td>
</tr>
</tbody>
)
}
}
Parent Component:
class ParentComponent extends React.PureComponent{
render(){
return(
<div className='table-center' id='table1'>
<table className="rwd-table" id="tblBlah" >
<tbody>
<tr>
<th>A</th>
<th>B</th>
<th>C</th>
<th>D</th>
<th>E</th>
<th>F</th>
<th>G</th>
<th> </th>
</tr>
</tbody>
<ChildComp/>
</table>
</div>
)
}
}
It sounds like you want to clone rows after the button is clicked.. Let me know if this is what you are looking for..
Hope this helps!
class ParentComponent extends React.Component {
render() {
return (
<div>
<table>
<tbody>
<tr>
<th>A</th>
<th>B</th>
<th>C</th>
<th>D</th>
<th>E</th>
<th>F</th>
<th>G</th>
<th> </th>
</tr>
</tbody>
<ChildComp
rows={[
[1, 2, 3, 4, 5, 6, 7],
[8, 9, 10, 11, 12, 13, 14],
[14, 15, 16, 17, 18, 19, 20]
]}
/>
</table>
</div>
);
}
}
class ChildComp extends React.Component {
state = {
tableRows: []
};
componentDidMount() {
this.setState({ tableRows: this.props.rows });
}
addNewRow = (rowToClone, index) => event => {
let newRows = [...this.state.tableRows];
newRows.splice(index, 0, rowToClone.map(i => `${i}` + `${i[0] || i}`));
this.setState({ tableRows: newRows });
};
render() {
return (
<tbody>
{this.state.tableRows.map((row, index) => {
return (
<tr>
{row.map(i => <td>{i}</td>)}
<td>
<button onClick={this.addNewRow(row, index + 1)}>
Clone Row
</button>
</td>
</tr>
);
})}
</tbody>
);
}
}
ReactDOM.render(<ParentComponent />, document.body);
table, th, tr, td {
border: 1px solid black;
}
table {
border-collapse: collapse;
}
td {
width: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
I think you are over-complicating the logic for your component-tree. This is how I interpreted your question.
You have a Parent component that renders a table with headers.
Each Child component is a table row.
You have a button that should add more rows (Child components).
The table cells belonging to each row should be editable (hence
inputs).
See working sandbox: https://codesandbox.io/s/inspiring-wave-bpv7f
It makes the most sense to have the Parent component manage the data-set. So all logic that involves updating or adding something from the data-set should belong to the Parent, not the Child components.
Consider an example like this;
Parent.js
class App extends React.Component {
state = {
headers: ["name", "age", "job", "salary"],
data: [
[
{ name: "Bobby Hill", editting: false },
{ age: 13, editting: false },
{ job: "student", editting: false },
{ salary: 0, editting: false }
]
]
};
createHeaders = () => {
const { headers } = this.state;
return headers.map(item => <th className="table-header">{item}</th>);
};
createBody = () => {
const { data } = this.state;
return data.map((row, index) => (
<Child data={row} rowIndex={index} toggleEdit={this.toggleEdit} />
));
};
addRow = () => {
const { data } = this.state;
const rowTemplate = [
{ name: "", editting: true },
{ age: "", editting: true },
{ job: "", editting: true },
{ salary: "", editting: true }
];
this.setState({
data: [...data, rowTemplate]
});
};
toggleEdit = (rowIndex, cellIndex) => {
const dataCopy = [...this.state.data];
const itemToUpdate = dataCopy[rowIndex][cellIndex];
itemToUpdate.editting = !itemToUpdate.editting;
this.setState({
data: dataCopy
});
};
render() {
return (
<div className="table-center" id="table1">
<table className="rwd-table" id="tblBlah">
<tbody>
<tr>{this.createHeaders()}</tr>
{this.createBody()}
</tbody>
</table>
<button onClick={this.addRow}>Add Row</button>
</div>
);
}
}
In the Parent component we have:
A data-set in our state which is an array of arrays. Each array
pertains to table-row.
When we iterate over the data-set, we pass in an array to each Child
component, which will render the row.
The addRow function will add a new array to our data-set,
containing objects corresponding to each table-header/property.
The toggleEdit function helps us swap between edit modes for each
cell.
Child.js
import React from "react";
const Child = ({ data, rowIndex, toggleEdit }) => {
return (
<tr>
{data.map((cell, cellIndex) => (
<td
className="table-cell"
onDoubleClick={() => toggleEdit(rowIndex, cellIndex)}
>
{cell.editting ? (
<input value={Object.values(cell)[0]} />
) : (
<span>{Object.values(cell)[0]}</span>
)}
</td>
))}
</tr>
);
};
export default Child;
Now in the Child component:
It consumes the data (array) that was passed to it was a prop and
uses it to render the row.
For each item (object) in the array, it creates a table-cell to
display the value.
Double-clicking the cell will toggle the edit property of the item belonging to the parent-state.
If editting is true, than an input is displayed, if not just a
normal table-cell.

How to display a selected element from dropdown menu

I would like to display a certain RSS feed depending on what item you select from the dropdown menu. At the minute when I select the item it will display the link on the screen but it will not update the state.
import React, { Component } from 'react'
class FacebookRSSDropdown extends Component {
state = {
links: [{id: 1, name: "Lost and Found Pets Northern Ireland" , content: "https://www.facebook.com/PetsLostandFoundNorthernIreland/"},
{id: 2, name: "USPCA", content: "https://www.facebook.com/UlsterSPCA/"},
{id: 3, name: "Pawboost Northern Ireland", content: "https://www.facebook.com/Northern-Ireland-Lost-Dogs-Cats-Pets-147512902479398/"},
{id: 4, name: "Assisi Animal Sanctuary", content: "https://www.facebook.com/AssisiAnimalSanctuary/"},
{id: 5, name: "Pets Reunited Newry and Mourne", content: "https://www.facebook.com/PetsReunitedNewryAndMourne/"}
],
linkClicked: [{
content: ''
}]
}
handleChange = (e) => {
console.log(e.target.value);
this.setState({
linkClicked: e.target.value
})
}
render() {
return (
<div className="container-fluid">
<h1> Facebook RSS Feeds </h1>
<select className="form-control" onClick={this.handleChange}>
{this.state.links && this.state.links.map(link => {
return (
<option value={link.content}>{link.name}</option>
)
})}
</select>
<div id="rssfeeds" className="row">
<div className="col">
<div className="fb-page"
data-tabs="timeline,events,messages"
data-href={this.state.linkClicked.content}
data-width="500"
data-height="850"
data-hide-cover="false">
</div>
</div>
</div>
</div>
)
}
}
export default FacebookRSSDropdown
For a <select> you should register a listener for onChange rather than onClick
handleChange = ev => {
this.setState({
linkClicked: ev.target.value
})
}
<select className="form-control" onChange={this.handleChange}>

Resources