radio button input controlling in react js - javascript

I have a form which has both boolean and string data to be shown in. I was able to control text fields with the help of state, but boolean fields are controlled with the help of enable and disable radio button. Was not able to control the boolean fields as there are many to be done. Is there any way that I can get the inputs for the fields?
import React, { useState } from 'react';
import moment from 'moment';
export const ConfigurationPage = (props) => {
const { t } = useTranslation();
const [configResp, setConfigResp] = useState({});
const [configSet,setConfigSet]=useState(null);
const [checked,isChecked]=useState(false);
React.useEffect(() => {
fetchConfig();
}, []);
React.useEffect(() => {
setInputConfig();
}, [configResp]);
const setInputConfig=()=>{
setConfigSet(configResp);
}
const fetchConfig = async() => {
try {
const resp = await APIEndpoint.get(`customer/app/config`);
if(resp.success){
const {result}=resp;
setConfigResp(result);
}
}
catch (resp) {
console.log(resp.msg)
}
}
const onChange = (e) => {
setConfigSet({[e.target.name]: e.target.value})
}
const getDate=()=>{
return moment().subtract(1, 'days').format('YYYY-MM-DD')+"T18:30:00.000Z"
}
return (
<div className="overlay" role="dialog">
<div className="dialog sm-scrollbar">
<div className="dialog__content">
<h2 className="dialog__title subheading-text-medium fontMedium">{preCompile.title}</h2>
<hr />
<div class="dialog__body_container flexRow justifyContentSpaceBetween flexWrap">
<div class="flexCol justifyContentSpaceEvenly ">
<div class=" dialog__container_item input-container">
<div class="dialog__description">
<label class="req_conf">Account Alias</label>
<input
id={''}
name="accountAlias"
onChange={(e)=>onChange(e)}
value={configSet?configSet.accountAlias:""}
type={'text'}
className='edit-input'
/>
</div>
</div>
</div>
<div class="flexCol justifyContentSpaceEvenly ">
<div class=" dialog__container_item input-container">
<div class="dialog__description">
<label class="req_conf">mSwipe</label>
<>
<div className={`dialog__description__radio`}>
<label>Enable</label>
{configSet.mswipeEnabled?<input id="" type="radio"
name="mswipeEnabled"
value={configSet.mswipeEnabled}
checked={configSet?.mswipeEnabled}
onChange={(e)=>onChange(e)} />:<input id="" type="radio"
name="mswipeEnabled"
value={!configSet.mswipeEnabled}
checked={!configSet?.mswipeEnabled}
onChange={(e)=>onChange(e)} />}
<label>Disable</label>
<input id="" type="radio"
name="mswipeEnabled"
value={configSet?configSet.mswipeEnabled:checked}
checked={configSet?.mswipeEnabled}
onChange={(e)=>onChange(e)} />
</div>
</>
</div>
</div>
</div>
</div>
<div className="dialog__footer flexCol alignItemsCenter">
<button className="done-button" onClick={props.onSubmit}>{props.title === 'Edit Item' ? 'Update' : 'Save'}</button>
</div>
</div>
</div>
);
}
Here I am trying to change values of mswipe fields which is a boolean with enable and disabled. Is there any way to do this?

Related

Dynamic Form in react with dynamic fields

I did looking for but i did not find an answer for my problem.
I need to create a dynamic form with dynamic fields, with the order that the user need.
import { useState } from "react";
const DemoLateral = () => {
const itemDataObject = {
title_item_lateral: '' ,
text_item_lateral: [],
image_lateral: [
{
title_image_lateral: '',
path_image_lateral: '',
}
],
document_lateral: [],
links: [
{
title_link:'' ,
link: ''
}
]
};
const addFields = () => {
let newItemField;
newItemField = itemDataObject;
setItems([...items, newItemField]);
};
const [items, setItems] = useState([]);
const [select, setSelect] = useState([]);
console.log(items);
console.log('select: ', select);
const handleChange = () => {
//let index =
//let name = items[i][e.target.name]=[e.target.value];
console.log();
};
const submitForm = (e) => {
e.preventDefault();
};
console.log(select);
return (
<>
<h3 className="ms-5 mb-5"> AÑADIR ITEMS </h3>
<div className="container">
<form onSubmit={submitForm} className=''>
<div>
{items.map((input, i)=> (
<>
<div className="row align-items-center row mb-4" key={i}>
<label htmlFor="exampleFormControlSelect1">Selecciona el Campo</label>
<div className="col-2" key={i}>
<select className="form-control" id="exampleFormControlSelect1"
onChange={(e) => setSelect([select[i]=e.target.value])} key={i}>
<option>Subtitulo</option>
<option>Imagen</option>
<option>Link</option>
<option>Texto</option>
</select>
</div>
<div className='col-8'>
<input
placeholder="desde From"
id={i}
className='form-control'
value={select[i]}
onChange= {handleChange(i)}
type="text"
required
/>
</div>
<button className="btn btn-danger col-1" >Borrar</button>
</div>
</>
))
}
</div>
<button className="btn btn-success me-4 mt-5" type='submit'>AddSubmit</button>
</form>
<div className="mt-5 text-center">
<button className="btn btn-primary me-4 mb-4" value='items' onClick={addFields}>Add Items</button>
</div>
</div>
</>
);
};
export default DemoLateral;
with this code i try to create a dynamic form with fields, that would be set in the form like the user need:
p.e:
'subtitle'
'image'
'text'
'text'
'link'
'image'
for this i create a select to choose the type of field, and try to put the select in the attribute name, for then when submit all works.
But i can not achieve. :-(
Where is my wrong....
maybe there are other way to do the same kind of form?

react js firestore firebase - Get subcollection document id with arrays to read data and post it

this is my when i add a question on a same document link
this is add_question.jsx
import "./newQuestion.scss"
import Sidebar from '../../components/sidebar/Sidebar'
import Navbar from '../../components/navbar/Navbar'
import { useState } from 'react'
import { db } from '../../firebase'
import { setDoc,addDoc, collection , doc} from '#firebase/firestore'
import { useParams , useNavigate} from 'react-router-dom';
const NewQuestion = () => {
const [question, setQuestion]=useState("");
const [correctanswer, setCorrectAnswer]=useState("");
const [optionone,setOptionOne]=useState("")
const [optiontwo,setOptionTwo]=useState("");
const [optionthree,setOptionThree]=useState("");
let params=useParams()
const navigate = useNavigate()
function handleQuestion(e){
e.preventDefault();
if (question ===''){
alert("please add question")
return
}
const examcollref = collection(db,`Exams/${params.id}/qna`);
addDoc(examcollref,{
question:question,
correctanswer:correctanswer,
incorrectanswer:[
optionone,
optiontwo,
optionthree
]
}).then(response => {
alert("question added")
navigate(-1)
}).catch(error =>{
console.log(error.message)
console.log(params.id)
})
}
return (
<div className="newQuestion">
<Sidebar/>
<div className="newQuestionContainer">
<Navbar/>
<div className="leftContainer">
<div className="leftTitle">
Add Question
</div>
<br />
<form onSubmit={handleQuestion}>
<label htmlFor="">Questions</label>
<input type="text"
placeholder="question"
onChange={e =>setQuestion(e.target.value)}
value={question} />
<h6>Input words for choices</h6>
<label htmlFor="">Choice A</label>
<input type="text"
placeholder='Input Choice A'
onChange={e => setOptionOne(e.target.value)}
value={optionone}/>
<label htmlFor="">Choice B</label>
<input type="text"
placeholder='Input Choice B'
onChange={e => setOptionTwo(e.target.value)}
value={optiontwo}/>
<label htmlFor="">Choice C</label>
<input type="text"
placeholder='Input Choice C'
onChange={e => setOptionThree(e.target.value)}
value={optionthree}/>
<label htmlFor="">Correct Answer</label>
<input type="text"
placeholder='Correct Answer'
onChange={e => setCorrectAnswer(e.target.value)}
value={correctanswer}/>
<button button="submit">Add</button>
</form>
</div>
</div>
</div>
)
}
export default NewQuestion
[enter image description here][1]
this is the model of my collection now. i want to get the questions and choices to post on my manageexam.jsx note that the choices are arrays.
manageexam.jsx
import React, { useEffect, useState , useRef} from 'react'
import Sidebar from '../../components/sidebar/Sidebar'
import Navbar from '../../components/navbar/Navbar'
import "./manageExam.scss"
import { Examtable } from "../../components/datatable/Examtable"
import { getDocs, getDoc , collection , onSnapshot, deleteDoc,updateDoc, doc,where,query,orderBy} from '#firebase/firestore'
import { db } from '../../firebase'
import {Link} from "react-router-dom"
import { useParams } from 'react-router-dom';
const ManageExam = () => {
const {id}=useParams()
const isMounted = useRef()
const [description,setDesc]=useState("")
const [title,setTitle]=useState("")
const [questions,setquestions] = useState("")
const [choice, setChoices] = useState("")
// const [currentId,setCurrentId]=useState([])
// const collectionRef = collection(db, "qna",id)
function handleUpdate(e){
e.preventDefault();
const examcollref = doc(db,"Exams",id)
updateDoc(examcollref,{
title:title,
description:description
} ).then(response => {
alert("updated")
console.log(getDoc(id))
}).catch(error =>{
console.log(error.message)
})
}
const getData = () => {
const document = doc(db, "Exams", id)
onSnapshot(document, (docs) => {
setTitle(docs.data().title)
setDesc(docs.data().description)
})
}
useEffect(() => {
if(isMounted.current){
return
}
isMounted.current = true;
getData()
}, [])
//get questions and choices,
// const getDatas = () => {
// onSnapshot(collectionRef, (data) => {
// console.log(data.docs.map((doc) => {
// return {...doc.data(), id: doc.id}
// }))
// })
// }
// useEffect(() => {
// if(isMounted.current){
// return
// }
// isMounted.current = true;
// getDatas()
// }, [])
return (
<div className="manageExam">
<Sidebar/>
<div className="manageExamContainer">
<div className="examInfo">
<h1>Manage Exam</h1>
</div>
<div className="left">
<div className="leftForm">
<div className="leftTitle">
Exam information
</div>
<br />
<form action="" onSubmit={handleUpdate}>
<textarea
id="desc"
cols="30"
rows="2"
placeholder="title"
value={title}
onChange={e => setTitle(e.target.value)}
></textarea>
<textarea
id="desc"
cols="30"
rows="7"
value={description}
onChange={e => setDesc(e.target.value)}
></textarea>
<button type="submit">Update</button>
</form>
</div>
</div>
<div className="right">
<div className="rightForm">
<div className="rightTitle">
Add Questions
<Link to= {`add_question/${id}`}style={{textDecoration:"none"}} className="link" >
Add new
</Link>
</div>
THIS IS THE CONTAINER THAT I WANT TO POST
{/* <div className="rightContainer">
{questions.map((doc) => {
return (
<div>
<p>{doc.questions}</p>
</div>
)
})}
<div>
<input type="radio" name="option1" value="database" checked/>
<label htmlFor="">database</label>
</div>
<div>
<input type="radio" name="option2" value="data" disabled/>
<label htmlFor="">data</label>
</div>
<div>
<input type="radio" name="option3" value="databytes" disabled/>
<label htmlFor="">databytes</label>
</div>
<div>
<input type="radio" name="option4" value="databoard" disabled/>
<label htmlFor="">databoard</label>
</div>
<br />
<button>update</button>
<button>delete</button>
</div> */}
</div>
</div>
</div>
</div>
)
}
export default ManageExam
this is what my database look like
[1]: https://i.stack.imgur.com/CgQTC.png

How Can i send image and text user information in react js

Sign up Form Written in JSX
import './SignUp-Form-CSS.css'
import { Link } from 'react-router-dom';
import { useState, useContext } from 'react';
import AuthContext from '../Context_store/AuthContext/AuthContext';
const SignUp = () => {
const [name, setName] = useState(null);
const [password, setPassword] = useState(null);
const [confirmPassword, setConfirmPassword] = useState(null);
const [image, setImage] = useState(null);
const authCtx = useContext(AuthContext);
const nameChangeHandeler = (event) => {
setName(event.target.value)
}
const passwordChangeHandeler = (event) => {
setPassword(event.target.value)
}
const confirmPasswordChangeHandeler = (event) => {
setConfirmPassword(event.target.value);
}
const imageChangeHandeler = (event) => {
setImage(event.target.files[0]);
console.log(event.target.files[0]);
}
const onSubmitHandeler = (event) => {
event.preventDefault()
// const data = {
// username: name,
// password: password,
// confirmPassword: confirmPassword,
// image: image
// }
const data=new FormData();
data.append("name",name);
data.append("password",password);
data.append("confirmPassword",confirmPassword);
data.append("image",image);
// data.append('username',name);
console.log(data);
authCtx.signup(data)
}
return (
<div class="container">
<div class="row">
<div class="col-lg-10 col-xl-9 mx-auto">
<div class="card flex-row my-5 border-0 shadow rounded-3 overflow-hidden">
<div class="card-img-left d-none d-md-flex">
{/* <!-- Background image for card set in CSS! --> */}
</div>
<div class="card-body p-4 p-sm-5">
<h5 class="card-title text-center mb-5 fw-light fs-5">Register</h5>
<form onSubmit={onSubmitHandeler} encType='multipart/form-data' >
<div class="form-floating mb-3">
<input type="text" class="form-control"
id="floatingInputUsername"
onChange={nameChangeHandeler}
placeholder="myusername" required autofocus />
<label for="floatingInputUsername">Username</label>
</div>
{/* <div class="form-floating mb-3">
<input type="email" class="form-control" id="floatingInputEmail" placeholder="name#example.com" />
<label for="floatingInputEmail">Email address</label>
</div> */}
<hr />
<div class="form-floating mb-3">
<input type="password"
class="form-control"
onChange={passwordChangeHandeler}
id="floatingPassword" placeholder="Password" />
<label for="floatingPassword">Password</label>
</div>
<div class="form-floating mb-3">
<input type="password" class="form-control"
onChange={confirmPasswordChangeHandeler}
id="floatingPasswordConfirm" placeholder="Confirm Password" />
<label for="floatingPasswordConfirm">Confirm Password</label>
</div>
<div>
<label>Select Logo </label>
<input name='image' onChange={imageChangeHandeler} type="file" class="form-control my-4" id="logo" placeholder="Select Logo " />
</div>
<div class="d-grid mb-2">
<button class="btn btn-lg btn-primary btn-login fw-bold text-uppercase" type="submit">Register</button>
</div>
<a class="d-block text-center mt-2 small" >Have an account?<Link class="nav-link" to={'/login'}> Sign In</Link></a>
<hr class="my-4" />
<div class="d-grid mb-2">
<button class="btn btn-lg btn-google btn-login fw-bold text-uppercase" type="submit">
<i class="fab fa-google me-2"></i> Sign up with Google
</button>
</div>
<div class="d-grid">
<button class="btn btn-lg btn-facebook btn-login fw-bold text-uppercase"
// onClick={onSubmitHandeler}
type="submit">
<i class="fab fa-facebook-f me-2"></i> Sign up with Facebook
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
)
}
export default SignUp;
CONTEXT API
import React, { useState } from "react";
import AuthContext from "./AuthContext";
const AuthContextProvider = (props) => {
const [token, setToken] = useState(null);
const login = (loginDetails) => {
}
const signUp = (signUpDetails) => {
console.log("Sign Up Called ");
fetch('http://localhost:5000/register',
{
method:'POST',
body:signUpDetails
// body:JSON.stringify(signUpDetails),
// headers:{
// 'Content-Type': 'application/json'
// }
}).then((resp) => {
return resp.json()
}).then((data) => {
console.log(data);
return data;
}).catch((err) => {
console.log(err);
})
}
const logout = () => {
}
const values = {
login: login,
signup: signUp,
logout: logout,
token: {
token: token,
setToken: setToken
},
isLoggedIn: !!token
}
return (
<div>
<AuthContext.Provider value={values}>
{props.children}
</AuthContext.Provider>
</div>
)
}
export default AuthContextProvider;
But When AT Node server End All Other filed Is recieved as Empty Except image Data
OUTPUT OF DATA SAVED IN DATABASE
And If I follow the Approach in Which I use Simple Object as the data instead of form Data(), I and with header ( that are under // ) I do not receive images at the backend and only receive user info
One solution could be to send the image as a string (base64) to the backend to save the image.
try to implement this in your react:
import { useState } from "react";
import "./styles.css";
export default function App() {
const [img, setImg] = useState("Image string will come here");
const handleChangeImg = (e) => {
console.log(e.target.files[0]);
const reader = new FileReader();
reader.readAsDataURL(e.target.files[0]);
reader.onloadend = () => {
setImg(reader.result);
};
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<input onChange={handleChangeImg} type="file" name="image" />
<button onClick={()=>setImg("Image string will come here")}>Reset</button>
<h1>{img}</h1>
</div>
);
}
Check the code iin codesandbox here.
Related resources:
FileReader

How to update new value as selected value in Formik multiselect field in reactjs?

I want to update the formik multiselect field. When I added new value to API and after, the new value set as selected value in formik multiselect field
const [toppings, setToppings] = useState([]);
useEffect(() => {
if (mainStore.topping) {
mainStore.getToppings()
.then(([success, data]) => {
if (success) {
setToppings(data.toppings)
} else {
setErrorMsg(data)
}
})
}
},[])
{formik =>
<div className='p-col-12 p-md-6'>
<div className='p-grid form-control-group'>
<div className='p-col-12'>
<label>Toppings</label>
</div>
<div className='p-col-12'>
<div className="p-inputgroup">
<Field name='product_toppings' as={MultiSelect} optionLabel='name' optionValue='topping_id' options={toppings} filter={true} />
<Button icon="pi pi-plus" type='button' className="p-button-success" onClick={() => { mainStore.visible = true; mainStore.topping = true; mainStore.stopLoading = true }} />
</div>
</div>
<div className='p-col-12'>
<ErrorMessage name='product_toppings' component={FormErrorMsg} />
</div>
</div>
</div>}

Creating Multiple Buttons Using React Classes

I'm currently trying to return multiple buttons from a class using Reactjs. Currently I can get all the buttons to return but the onClick function will only work on the last button to be displayed. If anyone can help its greatly appreciated. Here are the important lines of code.
let GroupCollection2 = db.collection('groups');
GroupCollection2.get()
.then(snapshot => {
snapshot.forEach(doc => {
if(doc.get('ModuleCode') === document.getElementById("groupSearch").value)
{
document.getElementById("groupDisplayError").innerHTML = "";
if(found === false){
document.getElementById("groupDisplayTable").innerHTML = '<tr id="groupDisplayTableHeader"><th>Group Name</th><th>Module Code</th><th>Join Or View Group</th></tr>';
}
found = true;
document.getElementById("groupDisplayTable").innerHTML += "<tr><td>"+doc.id+"</td><td>"+doc.data().ModuleCode+"</td><td class='groupDisplayTableButton'></td></tr>";
ReactDOM.render(<Button command={doc.id} />, document.getElementsByClassName('groupDisplayTableButton')[count]);
count++;
}
});
if(found === false){
document.getElementById("groupDisplayError").innerHTML = "No Results.";
}
})
.catch(err => {
console.log('Error getting documents', err);
});
and
class Button extends React.Component{
joinGroup(command){
alert(command);
}
render(){
return(<button onClick={this.joinGroup.bind(this, this.props.command ) }>Join Group</button>);
}
}
The entire code is here:
import React from "react";
import ReactDOM from 'react-dom';
import { compose } from "redux";
import { connect } from "react-redux";
import { signout } from "../store/actions/auth";
import requireAuth from "./hoc/requireAuth";
import firebase from "../services/firebase.js";
import Calendar from "./Planner.js"
//<Calendar />
var db = firebase.firestore();
const Main = ({ signout }) => {
return (
<div id="homePage" className="container">
<div className="row">
<div class="col s6" id="createPage">
<form id="createGroup">
<i className="large material-icons prefix search-icon">group_add</i>
<div className="row">
<div className="col s12">
<div className="row">
<div className="input-field col s12 vert-align">
<input type="text" id="cgroupName" name="groupName"/>
<label htmlFor="cgroupName">Group Name</label>
</div>
</div>
<div className="row">
<div className="input-field col s12 vert-align">
<input type="text" id="cgroupModuleCode" name="moduleCode"/>
<label htmlFor="cgroupModuleCode">Module Code</label>
</div>
</div>
<div className="row">
<input type="button" value="Create Group" onClick={ ()=> createGroup()}/>
</div>
<p id="groupCreateError"></p>
</div>
</div>
</form>
</div>
<div className="col s6">
{/*<script src="https://www.gstatic.com/firebasejs/3.1.0/firebase-database.js"></script>*/}
{/* Renders the search bar */}
<i className="large material-icons prefix search-icon">group</i>
<div className="row">
<div className="col s12">
<div className="row">
<div className="input-field col s12 vert-align">
<i className="material-icons prefix search-icon">search</i>
<input type= "text" id ="groupSearch" name="searchGroup"/>
<label htmlFor="groupSearch">Search For A Group</label>
<a className="waves-effect waves-teal btn-flat search-btn" onClick={ ()=> searchGroups()}>Search</a>
</div>
</div>
</div>
</div>
{/* Display any searched groups here */}
<div class="row" id="groupDisplay">
<p id="groupDisplayError"></p>
<table id="groupDisplayTable">
</table>
</div>
</div>
</div>
<button onClick={ () => profile()} hidden>Profile</button>
<button className="btn-switch" onClick={() => signout()}>Log Out</button>
</div>
);
};
function mapStateToProps(state) {
return {
auth: state.firebaseReducer.auth
};
}
function mapDispatchToProps(dispatch) {
return {
signout: () => dispatch(signout())
};
}
function profile(){
}
function logOut(){
document.getElementById("navbar").style.display = "none";
signout();
}
function searchGroups(){
if(document.getElementById("groupSearch").value === ""){
document.getElementById("groupDisplayError").innerHTML = "Please enter a value and try again.";
}
else{
var found = false;
var count = 0;
let GroupCollection = db.collection('groups').doc(document.getElementById("groupSearch").value);
GroupCollection.get()
.then(doc => {
if (doc.exists) {
found = true;
document.getElementById("groupDisplayError").innerHTML = "";
document.getElementById("groupDisplayTable").innerHTML = '<tr id="groupDisplayTableHeader"><th>Group Name</th><th>Module Code</th><th>Join Or View Group</th></tr>';
document.getElementById("groupDisplayTable").innerHTML += "<tr><td>"+doc.id+"</td><td>"+doc.data().ModuleCode+"</td><td class='groupDisplayTableButton'></td></tr>";
ReactDOM.render(<Button command={doc.id}/>, document.getElementsByClassName('groupDisplayTableButton')[count]);
count++;
}
})
.catch(err => {
document.getElementById("groupDisplayError").innerHTML = "Error getting document: "+err;
});
let GroupCollection2 = db.collection('groups');
GroupCollection2.get()
.then(snapshot => {
snapshot.forEach(doc => {
if(doc.get('ModuleCode') === document.getElementById("groupSearch").value)
{
document.getElementById("groupDisplayError").innerHTML = "";
if(found === false){
document.getElementById("groupDisplayTable").innerHTML = '<tr id="groupDisplayTableHeader"><th>Group Name</th><th>Module Code</th><th>Join Or View Group</th></tr>';
}
found = true;
document.getElementById("groupDisplayTable").innerHTML += "<tr><td>"+doc.id+"</td><td>"+doc.data().ModuleCode+"</td><td class='groupDisplayTableButton'></td></tr>";
ReactDOM.render(<Button command={doc.id} />, document.getElementsByClassName('groupDisplayTableButton')[count]);
count++;
}
});
if(found === false){
document.getElementById("groupDisplayError").innerHTML = "No Results.";
}
})
.catch(err => {
console.log('Error getting documents', err);
});
}
}
function createGroup(){
let GroupCollection = db.collection('groups').doc(document.getElementById("cgroupName").value);
GroupCollection.get()
.then(doc => {
if (!doc.exists) {
document.getElementById("groupCreateError").innerHTML = "Group created sucessfully.";
const GroupCollection2 = db.collection('groups');
GroupCollection2.doc(document.getElementById("cgroupName").value).set({
ModuleCode:document.getElementById("cgroupModuleCode").value,
Timetable: "",
User0: firebase.auth().currentUser.email,
User1: "",
User2: "",
User3: "",
User4: "",
User5: "",
User6: "",
User7: "",
User8: "",
User9: "",
})
} else {
document.getElementById("groupCreateError").innerHTML = "Group Name Already Exists.";
}
})
.catch(err => {
document.getElementById("groupCreateError").innerHTML = "Error getting document: "+err;
});
}
class Button extends React.Component{
joinGroup(command){
alert(command);
}
render(){
return(<button onClick={this.joinGroup.bind(this, this.props.command ) }>Join Group</button>);
}
}
export default compose(
connect(
mapStateToProps,
mapDispatchToProps
),
requireAuth
)(Main);
You may have to make sure each button as a unique key. In this case, using the doc.id may work. So, you would want to add a key prop as below:
ReactDOM.render(<Button command={doc.id} key={doc.id} />,
document.getElementsByClassName('groupDisplayTableButton')[count]);
If there's any chance that you will have multiple of the same doc.id, you could modify the key to include the count to ensure each button has a unique key. i.e key={doc.id + count}
I don't think you are binding the joinGroup function properly. Try replacing your Button class with the following functional component:
const Button = ({ command }) => {
const joinGroup = _command => alert(_command);
return (
<button type="button" key={command} onClick={() => joinGroup(command)}>
Join Group
</button>
);
};
Unfortunately, I don't have a solution. That said, I definitely think this line is the problem: ReactDOM.render(<Button command={doc.id} />, document.getElementsByClassName('groupDisplayTableButton')[count]);.
Do not use ReactDOM.render inside the app, render the buttons to their parent, in this case, the table. You can't exactly do that since the table doesn't exist because you are manually adding elements to the DOM.
In the process of trying to figure out what is going on, I ended up refactoring half your code. Thought I'd share it in case it helps at some point.
Here is a sandbox to show that the click handler works with multiple table buttons.
I removed the search function and mocked out firebase.
import React, { useState } from "react";
import ReactDOM from "react-dom";
function App() {
return <Main />;
}
const Main = () => {
// fake firebase mock
const [firebaseMock, setFirebaseMock] = useState("");
// input fields
const [groupName, setGroupName] = useState("");
const [moduleCode, setModuleCode] = useState("");
// error messages
const [errorMessage, setErrorMessage] = useState("");
const createGroup = (_groupName, _moduleCode) => {
// ensure user submist a group name and a module code
if (!_groupName || !_moduleCode) {
setErrorMessage("You must provide a group name and a module code.");
return;
}
// if group already exists with the same name, create an error message
if (Object.keys(firebaseMock).includes(_groupName)) {
setErrorMessage("Group Name Already Exists.");
return;
}
setFirebaseMock({
...firebaseMock,
[_groupName]: { ModuleCode: _moduleCode }
});
// clear inputs
setGroupName("");
setModuleCode("");
};
return (
<div id="homePage" className="container">
<div className="row">
<div class="col s6" id="createPage">
<form id="createGroup">
<i className="large material-icons prefix search-icon">group_add</i>
<div className="row">
<div className="col s12">
<div className="row">
<div className="input-field col s12 vert-align">
<input
type="text"
id="cgroupName"
name="groupName"
value={groupName}
onChange={e => setGroupName(e.target.value)}
/>
<label htmlFor="cgroupName">Group Name</label>
</div>
</div>
<div className="row">
<div className="input-field col s12 vert-align">
<input
type="text"
id="cgroupModuleCode"
name="moduleCode"
value={moduleCode}
onChange={e => setModuleCode(e.target.value)}
/>
<label htmlFor="cgroupModuleCode">Module Code</label>
</div>
</div>
<div className="row">
<input
type="button"
value="Create Group"
onClick={() => createGroup(groupName, moduleCode)}
/>
</div>
<p id="groupCreateError">{errorMessage}</p>
</div>
</div>
</form>
</div>
<div class="row" id="groupDisplay">
{!!Object.keys(firebaseMock).length && (
<table id="groupDisplayTable">
<thead>
<tr id="groupDisplayTableHeader">
<th>Group Name</th>
<th>Module Code</th>
<th>Join Or View Group</th>
</tr>
</thead>
<tbody>
{Object.keys(firebaseMock).map(groupId => (
<tr>
<td>{groupId}</td>
<td>{firebaseMock[groupId].ModuleCode}</td>
<td class="groupDisplayTableButton">
<Button command={groupId} />
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
</div>
</div>
);
};
const Button = ({ command }) => {
const joinGroup = _command => console.log(_command);
return (
<button type="button" key={command} onClick={() => joinGroup(command)}>
Join Group
</button>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
The only difference here is that none of my functions contains HTML, they just manipulate state. In the future, a simple way to think in react is to always make sure that HTML code only lives in the render function. That way everything else only manipulates data and state. This decoupling of the soul of an app (data) from the body of app (HTML)lets the HTML 'react' to changes in state, which is the whole point of the framework.

Categories

Resources