import React from 'react';
import 'materialize-css/sass/materialize.scss';
import 'materialize-css/js/materialize.js';
import 'font-awesome/scss/font-awesome.scss';
import '../styles/main.scss';
export default class AddStorageModal extends React.Component {
constructor() {
super();
this.state = { storageName: "", sharingKey: "" };
}
handleChange(event) {
this.setState({ [event.target.name]: event.target.value });
}
validate() {
if (this.state.storageName === "" && this.state.sharingKey == "") {
console.log("validation error");
return false;
}
this.props.createNewStorage(this.state);
}
resetForm() {
this.setState({ storageName: "", sharingKey: "" });
$(function () {
Materialize.updateTextFields();
});
}
render() {
if (this.props.storages.openAddStorageModal) {
$('#add-new-storage-modal').openModal({ dismissible: false });
}
else {
this.resetForm.bind(this);
$('#add-new-storage-modal').closeModal();
}
return (
<div id="add-new-storage-modal" className="modal" >
<div className="modal-content">
<h6>Enter your new Storage (Freezer, Pantry, etc.) </h6>
<div className="row">
<form>
<div className="input-field col s12 m12 l12 ">
<input id="storage_name" type="text" value={this.state.storageName} name="storageName" onChange={ (event) => this.handleChange(event) } />
<label htmlFor="storage_name">Storage Name</label>
</div>
<br />
<h4 className="center">OR</h4>
<h6>Enter in the sharing key you were given.</h6>
<div className="input-field col s12 m12 l12 ">
<input id="sharing_key" type="text" value={this.state.sharingKey} name="sharingKey" onChange={ (event) => this.handleChange(event) } />
<label htmlFor="sharing_key">Sharking Key</label>
</div>
</form>
</div>
</div>
<div className="modal-footer">
<a href="#!" className="waves-effect waves-green btn-flat left" onClick={() => this.validate() }>Add</a>
<a href="#!" className="waves-effect waves-green btn-flat" onClick={() => this.props.loadAddStorageModal(false) }>Cancel</a>
</div>
</div>
)
}
}
When "this.resetForm.bind(this);" gets hit, it does not execute it. It use to work but I don't know what I did but now it does not work.
I going to have to work backwards and remove what I did to see if I can figure it out but hoping someone might just know why.
That line doesn't do anything anyway. Function.prototype.bind returns a new function, but you're not doing anything with the returned function.
You'll have to be more clear about what you expect that line to do to get a better answer than this. Maybe you just meant to call resetForm like this:
this.resetForm( );
Related
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.
My problem is simple yet infuriating. All I want to do is have an onClick event that when triggered changes the state on the component. The only problem is if I don't stop the addActiveClass function from running initially with the loaded state prop than I get a call stack overload because this component is being rendered by a .map function.
The second I remove the if (this.state.loaded) { //logic } over my state change logic I get
Please help me understand why I can't do this simple thing in React.
class IndividualQA extends Component {
constructor(props) {
super(props);
// this.addActiveClass = this.addActiveClass.bind(this);
this.state = {
active: false,
loaded: false
};
// this.state({ arrowState: this.props.arrowClass });
}
addActiveClass = (load) => {
if (this.state.loaded) {
const currentState = this.state.active;
console.log(this.state);
this.setState({ active: !currentState });
}
};
render() {
return (
<div>
<div className="row">
<div className="col-xl-11 col-lg-11 col-md-11 col-sm-10 col-10">
{this.props.data.Q}
</div>
<div className="col-xl-1 col-lg-1 col-md-1 col-sm-2 col-2 Question">
<a
data-toggle="collapse"
href={`#QA${this.props.id}`}
id="arrowSVG"
onClick={this.addActiveClass()}
>
<img
style={{ height: "30px" }}
className={this.state.active ? "transform" : ""}
src={downArrow}
/>
</a>
</div>
</div>
<div className="row collapse" id={`QA${this.props.id}`}>
<div className="col-xl-11 col-lg-11 col-md-11 col-sm-11 col-11 As-a-long-time-Kiva">
{this.props.data.A}
</div>
</div>
<hr />
</div>
);
}
}
export default IndividualQA;
Your problem is in here
<a
data-toggle="collapse"
href={`#QA${this.props.id}`}
id="arrowSVG"
onClick={this.addActiveClass()} //here
>
This way addActiveClass will be called on every render and can cause a infinty loop.
You need to change it to
<a
data-toggle="collapse"
href={`#QA${this.props.id}`}
id="arrowSVG"
onClick={this.addActiveClass} // without '()'
>
The handler on the a tag should be this.addActiveClass not this.addActiveClass()
Sorry for my English, I'm not a native speaker so please don't minus me too much. I'm a beginner in programming and I'm learning from tutorials found on internet. Today is my first time asking a question on Stack Overflow. It's probably a silly question, I know there are many similar questions, but it's a different issue, it's not a duplicate. Let me move to my question.
I have a react component in which I'm using react-plaid npm package to use Plaid APi. it can be found here react-plaid
My current component code looks like this
Component
import React, {Component} from 'react';
import BuggyApi from "../../services/BuggyApi";
import BlockUi from "react-block-ui";
import ReactPlaid from 'react-plaid'
class Integration extends Component{
state={
plaid_public_key: "",
plaid_public_token: "",
blocking: false,
isSuccess: false,
errorMessage: [],
open: false,
plaidData: [],
};
componentWillMount(){
new BuggyApi().getPlaidPublicKey().then((response)=>{
console.log(response.data);
if(response.status===200){
this.setState({
plaid_public_key: response.data.public_key
});
}
}).catch((error)=>{
})
}
handleOnExit = (error, metadata)=>{
if (error != null) {
console.log('link: user exited');
console.log(error, metadata);
}
};
handleOnLoad =()=>{
console.log('link: loaded');
};
handleOnEvent =(eventname, metadata)=>{
console.log('link: user event', eventname, metadata);
};
handleOnSuccess = (public_token, metadata) => {
console.log('public_token: ' + public_token);
console.log('account ID: ' + metadata.account_id);
};
componentDidMount(){
const script = document.createElement("script");
script.src = "https://cdn.plaid.com/link/v2/stable/link-initialize.js";
script.async = true;
document.body.appendChild(script);
}
render(){
return(
<div className="page-wrapper">
<div className="content container-fluid">
<div className="row">
<div className="col-xs-8">
<h4 className="page-title">Integration</h4>
</div>
<div className="col-xs-4 text-right m-b-30">
</div>
</div>
<div className="row">
<div className="col-md-12">
<div className="text-center">
<h4 className="modal-title">
Link your bank account
</h4>
</div>
<br/>
<br/>
<form>
{(this.state.isSuccess)?
<div className="row">
<div className="col-sm-6 col-sm-offset-3">
<div className="alert alert-success">
<strong>Success!</strong> Settings updated successfully!
</div>
</div>
</div>:null
}
{(this.state.errorMessage.length>0)?
<div className="row">
<div className="col-sm-6 col-sm-offset-3">
<div className="alert alert-danger">
<ul>
{this.state.errorMessage.map((message,i) =><li key={i}>{message}</li>)}
</ul>
</div>
</div>
</div>:null
}
<BlockUi tag="div" blocking={this.state.blocking}>
<div className="row">
<div className="col-sm-6 col-sm-offset-3">
{(this.state.plaid_public_key!=="")?
<div>
<button onClick={() => this.setState({ open: true})}>Open Plaid</button>
<ReactPlaid
clientName="Arshad"
product={["auth"]}
apiKey={this.state.plaid_public_key}
env='sandbox'
open={this.state.open}
onSuccess={(token, metaData) => this.setState({plaidData: metaData})}
onExit={() => this.setState({open: false})}
/>
</div>:null
}
</div>
</div>
</BlockUi>
</form>
</div>
</div>
</div>
</div>
);
};
}
export default Integration;
The problem is when I click the open link button it just shows the Plaid model for few seconds and then refreshes the application page. I'm wondering if someone had the same and can help me out there.
Note:
Please ignore the public key state you can just set it to "c10c40c4ee5eee97764307027f74c2" in apiKey={this.state.plaid_public_key}. I'm getting the public key from the server using axious.
I think I found the issue and though it'd be OK to post my answer on stackoverflow to help others if anyone ever faces the same problem. I was putting the react-plaid-link inside the tags. The react-plaid-link returns a button which according to the html standard a button inside a form without "type" attribute acts like a submit button. Same goes here in my case which I click the the button it submit the form which causes refreshing page. I fixed the issue by just removing the tags. My updated code looks like this.
import React, {Component} from 'react';
import BuggyApi from "../../services/BuggyApi";
import BlockUi from "react-block-ui";
import ReactPlaid from 'react-plaid'
class Integration extends Component{
state={
plaid_public_key: "",
plaid_public_token: "",
blocking: false,
isSuccess: false,
errorMessage: [],
open: false,
plaidData: [],
};
componentWillMount(){
new BuggyApi().getPlaidPublicKey().then((response)=>{
console.log(response.data);
if(response.status===200){
this.setState({
plaid_public_key: response.data.public_key
});
}
}).catch((error)=>{
})
}
handleOnExit = (error, metadata)=>{
if (error != null) {
console.log('link: user exited');
console.log(error, metadata);
}
};
handleOnLoad =()=>{
console.log('link: loaded');
};
handleOnEvent =(eventname, metadata)=>{
console.log('link: user event', eventname, metadata);
};
handleOnSuccess = (public_token, metadata) => {
console.log('public_token: ' + public_token);
console.log('account ID: ' + metadata.account_id);
};
componentDidMount(){
const script = document.createElement("script");
script.src = "https://cdn.plaid.com/link/v2/stable/link-initialize.js";
script.async = true;
document.body.appendChild(script);
}
render(){
return(
<div className="page-wrapper">
<div className="content container-fluid">
<div className="row">
<div className="col-xs-8">
<h4 className="page-title">Integration</h4>
</div>
<div className="col-xs-4 text-right m-b-30">
</div>
</div>
<div className="row">
<div className="col-md-12">
<div className="text-center">
<h4 className="modal-title">
Link your bank account
</h4>
</div>
<br/>
<br/>
{(this.state.isSuccess)?
<div className="row">
<div className="col-sm-6 col-sm-offset-3">
<div className="alert alert-success">
<strong>Success!</strong> Settings updated successfully!
</div>
</div>
</div>:null
}
{(this.state.errorMessage.length>0)?
<div className="row">
<div className="col-sm-6 col-sm-offset-3">
<div className="alert alert-danger">
<ul>
{this.state.errorMessage.map((message,i) =><li key={i}>{message}</li>)}
</ul>
</div>
</div>
</div>:null
}
<BlockUi tag="div" blocking={this.state.blocking}>
<div className="row">
<div className="col-sm-6 col-sm-offset-3">
{(this.state.plaid_public_key!=="")?
<div>
<button onClick={() => this.setState({ open: true})}>Open Plaid</button>
<ReactPlaid
clientName="Arshad"
product={["auth"]}
apiKey={this.state.plaid_public_key}
env='sandbox'
open={this.state.open}
onSuccess={(token, metaData) => this.setState({plaidData: metaData})}
onExit={() => this.setState({open: false})}
/>
</div>:null
}
</div>
</div>
</BlockUi>
</div>
</div>
</div>
</div>
);
};
}
export default Integration;
Sometimes you need to put the Plaid button inside a form element, in which case, just pass (e) => e.preventDefault() in as the onClick handler
<ReactPlaid
clientName="Arshad"
product={["auth"]}
apiKey={this.state.plaid_public_key}
env='sandbox'
open={this.state.open}
onSuccess={(token, metaData) => this.setState({plaidData: metaData})}
onExit={() => this.setState({open: false})}
onClick={(e) => e.preventDefault()}
/>
First, I'm almost new to reactjs. I want to create a simple editing mask for getting deeper into reactjs.
What is the "Best Practice" for this situation?
I'm having a page, where you can simply add, change or delete a company entry.
What I want to achieve is, to open a modal dialog window, when I click on a company entry. In the modal dialog window then, the user can modify or delete the entry.
First I created a CompanyList component.
import React, { Component } from 'react';
import Company from './Company';
class CompanyList extends Component {
constructor(props) {
super(props);
this.state = {
search: '',
companies: props.companies
};
}
updateSearch(event) {
this.setState({ search: event.target.value.substr(0,20) })
}
addCompany(event) {
event.preventDefault();
let nummer = this.refs.nummer.value;
let bezeichnung = this.refs.bezeichnung.value;
let id = Math.floor((Math.random()*100) + 1);
$.ajax({
type: "POST",
context:this,
dataType: "json",
async: true,
url: "../data/post/json/companies",
data: ({
_token : window.Laravel.csrfToken,
nummer: nummer,
bezeichnung : bezeichnung,
}),
success: function (data) {
id = data.Nummer;
this.setState({
companies: this.state.companies.concat({id, nummer, bezeichnung})
})
this.refs.bezeichnung.value = '';
this.refs.nummer.value = '';
}
});
}
render() {
let filteredCompanies = this.state.companies.filter(
(company) => {
return company.bezeichnung.toLowerCase().indexOf(this.state.search.toLowerCase()) !== -1;
}
);
return (
<div>
<div className="row">
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">Search</div>
<div className="col-xs-12 col-sm-12 col-md-9 col-lg-9">
<div className="form-group">
<input className="form-control" type="text" value={this.state.search} placeholder="Search" onChange={this.updateSearch.bind(this)} />
</div>
</div>
</div>
<form onSubmit={this.addCompany.bind(this)}>
<div className="row">
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">Create new entry</div>
<div className="col-xs-12 col-sm-12 col-md-3 col-lg-3">
<div className="form-group">
<input className="form-control" type="text" ref="nummer" placeholder="New company no." required />
</div>
</div>
<div className="col-xs-12 col-sm-12 col-md-3 col-lg-3">
<div className="form-group">
<input className="form-control" type="text" ref="bezeichnung" placeholder="New company name" required />
</div>
</div>
<div className="col-xs-12 col-sm-12 col-md-3 col-lg-3">
<div className="form-group">
<button type="submit" className="btn btn-default">Add new company</button>
</div>
</div>
</div>
</form>
<div className="row">
<div className="col-xs-10 col-sm-10 col-md-10 col-lg-10">
<ul>
{
filteredCompanies.map((company)=> {
return (
<div>
<Company company={company} key={company.id} />
</div>
);
})
}
</ul>
</div>
</div>
</div>
);
}
}
export default CompanyList
The Company component looks like this:
import React, { Component } from 'react';
class Company extends Component {
constructor(props) {
super(props);
this.state = {
company: props.company,
onClick: props.onClick
};
}
render() {
return (
<div>
<li>
<div className="cursorPointer" >
{this.props.company.nummer} {this.props.company.bezeichnung}
</div>
</li>
</div>
);
}
}
export default Company
My issue is now, how and where to implement the modal dialog?
Is it best practice to create an own component for it e.g. CompanyOptions? Should it be part of Company or better one component added in CompanyList? But then, how to pass the current Company to the modal dialog.
Sorry, if I'm asking too many questions. But I want to find out how it is recommended in reactjs.
UPDATE
Now I've created an own component for it.
This component looks like this:
import React, { Component } from 'react';
class CompanyOptions extends Component {
constructor(props) {
super(props);
this.state = {
company: props.company,
css: props.css,
onClick: props.onClick
};
}
render() {
return (
<div>
<div className={this.state.css} tabindex="-1" role="dialog">
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 className="modal-title">Company entry "{this.state.company.bezeichnung}"</h4>
</div>
<div className="modal-body">
<p>One fine body…</p>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" className="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default CompanyOptions
In the Company component I render it this way:
render() {
return (
<div>
<li>
<div className="cursorPointer" onClick={this.toggleOptionFields.bind(this)}>
{this.props.company.nummer} {this.props.company.bezeichnung}
</div>
<CompanyOptions company={this.state.currentCompany} css={this.state.optionFieldsCss} />
...
I've created a state and a method for the click event:
constructor(props) {
super(props);
this.state = {
company: props.company,
onClick: props.onClick,
editFieldsCss: "displayNone",
optionFieldsCss: "modal fade",
currentCompany: props.company,
};
}
and the method:
toggleOptionFields() {
var css = (this.state.optionFieldsCss === "modal fade in displayBlock") ? "modal fade" : "modal fade in displayBlock";
this.setState({
"optionFieldsCss":css,
currentCompany: this.company
});
}
But when I click on the company the css in the component call is updated. But not in the component itself:
Why? Anybody an idea?
The best way is to create a new component for a modal. This way it would be reusable.
Then you can include it where you need it and you can send all company info via props to that modal.
Add an state property showModal and set it to false. Then onClick event change showModal to true. Then in your render method you can check if(this.state.showModal) and then show modal.
Your state :
constructor(props){
super(props);
this.state = {
showModal: false,
currentCompanyName: "",
currentCompanyNumber: ""
}
}
Then onClick event:
handleClick(currentCompanyName, currentCompanyNumber){
this.setState({
showModal: true,
currentCompanyName: currentCompanyName,
currentCompanyNumber: currentCompanyNumber
})
}
And then in your render:
render(){
if(this.state.showModal)
return <MyModal companyName={this.state.currentCompanyName} companyNumber={this.state.currentCompanyNumber} />
return (
//Rest of the code
)
}
I started playing with some React/Redux + t7 (in order to avoid any sort of transpiling), for the sake of learning.
When it all started making some sense to me, I encountered this voodooish issue, where the bounded onClick function sometimes fires and sometimes doesn't (?!)
As you can see, clicking the plus button doesn't always invoke the bounded function to onClick.
I'm using the latest version of Google Chrome (v53).
What the hell?
JS
'use strict';
const store = Redux.createStore(Redux.combineReducers({
todos: (state = [], action) => {
switch(action.type) {
case 'ADD_TODO':
return state.concat([action.payload]);
default:
return [];
}
}
}));
t7.module((t7) => {
t7.assign("AddTodo", React.createClass({
addTodo() {
console.log('clicked');
return store.dispatch({
type: 'ADD_TODO',
payload: {
text: this.refs.todoText.value,
}
})
},
render() {
return t7`
<div className="row">
<div className="col-xs-4 form-group-lg">
<input className="form-control" ref="todoText"/>
</div>
<div className="col-xs-2">
<button className="btn btn-lg btn-info">
<span className="glyphicon glyphicon-plus"
onClick="${this.addTodo}"
style=${{fontSize: 'large'}}>
</span>
</button>
</div>
</div>
`;
}
}));
t7.assign("TodoList", React.createClass({
render() {
return t7`
<div className="row">
<div className="col-xs-12">
<ul>
${store.getState().todos.map((todo, i) => t7`
<li key=${i}>${todo.text}</li>
`)}
</ul>
</div>
<div>
`;
}
}));
const render = () => ReactDOM.render(
t7`
<div className="container">
<div className="jumbotron">
<h1>Todos</h1>
</div>
<AddTodo />
<TodoList />
</div>
`, document.getElementById('root')
);
store.subscribe(render);
render();
});
Your Click event works whenver your click on the glyphicon plus and not outside it. The issue is that you have placed the onClick event at the wrong place add it to the button rather than the span and it will work
render() {
return t7`
<div className="row">
<div className="col-xs-4 form-group-lg">
<input className="form-control" ref="todoText"/>
</div>
<div className="col-xs-2">
<button className="btn btn-lg btn-info" onClick="${this.addTodo}">
<span className="glyphicon glyphicon-plus"
style=${{fontSize: 'large'}}>
</span>
</button>
</div>
</div>
`;
}