My isLoading flag never set true in the constructor() - reactjs

I'm working whith one representational component (ProjectFormUpdate) and his container (ProjectFormUpdateContainer). From the container, i send an document object Project and a flag isLoading. But in a Constructor() of ProjectFormUpdate, the flag is false... the state is never seted.
The representational componente
import React, { Component} from 'react';
import ReactDOM from 'react-dom';
import { Projects } from '/imports/api/projects.js';
import PropTypes from 'prop-types'; // ES6
import { withTracker } from 'meteor/react-meteor-data';
export default class ProjectFormUpdate extends Component {
handleUpdate(event) {
event.preventDefault();
console.log("se modificó el estadoooo")
this.setState({
codigo: ReactDOM.findDOMNode(this.refs.codigoInput).value.trim(),
nombre: ReactDOM.findDOMNode(this.refs.nombreInput).value.trim()
});
}
handleSubmit(event){
this.setState({
codigo: ReactDOM.findDOMNode(this.refs.codigoInput).value.trim(),
nombre: ReactDOM.findDOMNode(this.refs.nombreInput).value.trim()
});
}
constructor(props) {
super(props);
if (!props.isLoading){
this.state = {
codigo: props.oneProject.codigo,
nombre: props.oneProject.nombre}
}
else{
this.state = {
codigo: 'dd',
nombre: 'ff'}
}
}
render() {
const { oneProject, isLoading } = this.props;
if (!isLoading){
this.setState = {
codigo: this.props.oneProject.codigo,
nombre: this.props.oneProject.nombre}
return (
<div className="col-xs-11">
<div className="box box-solid">
<form className="form" onSubmit={this.handleSubmit.bind(this)} >
<div className="box-body">
<div className="row">
<div className="col-xs-2">
<input
className = "form-control input-sm"
type="text"
ref="codigoInput"
placeholder="Código del Proyecto"
value = {this.state.codigo}//this.state.codigo}
onChange = {this.handleUpdate.bind(this)}
/>
</div>
<div className="col-xs-6">
<input
className = "form-control input-sm"
type="text"
ref="nombreInput"
placeholder="Título"
value = {this.state.nombre }
onChange = {this.handleUpdate.bind(this)}
/>
</div>
</div>
</div>
<div className="box-footer">
<button type="submit" className="btn btn-sm btn-primary btn-flat">Guardar</button>
</div>
</form>
</div>
</div>
);
}
else {return (<div></div>);}
}}
ProjectFormUpdate.propTypes = {
// This component gets the task to display through a React prop.
// We can use propTypes to indicate it is required
oneProject: React.PropTypes.object,
isLoading: React.PropTypes.bool,
};
The Container
import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
import { Projects } from '/imports/api/projects.js';
import ProjectFormUpdate from './ProjectFormUpdate.jsx';
export default ProjectFormUpdateContainer = withTracker(({ key1 }) => {
const sub = Meteor.subscribe('projects');
var oneProject = Projects.findOne(key1);
var isLoading = !sub.ready();
return {
oneProject,
isLoading,
};
})(ProjectFormUpdate);
So... if i can't set the state, i can't set the form's values in a controled way. Any suggestion?

In order to set your components state outside of the constructor() function: you must call this.setState(). this.setState() will set it's first argument as the new state and subsequently call your component's render function.
Your if (!isLoading) statement is very dangerous. Assuming !isLoading == true: your render function will infinitely fire this.setState(), thereby locking your browser.
Your constructor function appears correct, as is. I would allow it to set the initial application state and handle the rest from within the render() function. Alternatively, you could set your initial state within the componentWillMount() or componentDidMount() functions found here.
Within your render() function, I would omit the if (!isLoading) part and instead try returning a loading component if (isLoading == true).
You can also apply the following logic directly to your <input/> elements to set your component's state with finesse:
<input value={this.state.key} onChange={(event) => this.setState({key: event.target.value})}/>
I've revised your ProjectFormUpdate component as follows:
import React, { Component} from 'react';
import ReactDOM from 'react-dom';
import { Projects } from '/imports/api/projects.js';
import PropTypes from 'prop-types'; // ES6
import { withTracker } from 'meteor/react-meteor-data';
export default class ProjectFormUpdate extends Component {
handleSubmit(event){
event.preventDefault()
console.log()
}
constructor(props) {
super(props);
if (!props.isLoading) {
this.state = {
codigo: props.oneProject.codigo,
nombre: props.oneProject.nombre
}
}
else {
this.state = {
codigo: '',
nombre: ''
}
}
}
render() {
const { oneProject, isLoading } = this.props;
if (isLoading) {
return (
<div>isLoading == true</div>
)
}
return (
<div className="col-xs-11">
<div className="box box-solid">
<form className="form" onSubmit={this.handleSubmit.bind(this)} >
<div className="box-body">
<div className="row">
{/* Codigo. */}
<div className="col-xs-2">
<input className = "form-control input-sm" type="text" ref="codigoInput" placeholder="Código del Proyecto" value={this.state.codigo} onChange={(event) => this.setState({codigo: event.target.value})} />
</div>
{/* Nombre. */}
<div className="col-xs-6">
<input className = "form-control input-sm" type="text" ref="nombreInput" placeholder="Título" value={this.state.nombre} onChange={(event) => this.setState({nombre: event.target.value}))} />
</div>
</div>
</div>
<div className="box-footer">
<button type="submit" className="btn btn-sm btn-primary btn-flat">Guardar</button>
</div>
</form>
</div>
</div>
)
}
ProjectFormUpdate.propTypes = {
oneProject: React.PropTypes.object,
isLoading: React.PropTypes.bool,
};

Related

Render an array of objects react

This is parent object:
import React, { Component } from "react";
import uuid from 'react-uuid';
import Context from "../../context/Context";
import ItemInput from "./input-components/ItemInput";
export default class InputContainer extends Component {
constructor(props) {
super(props);
this.state = {
name: '',
item: {
id: '',
toDo: ''
},
listItems: []
};
this.onItemChange = this.onItemChange.bind(this);
this.onItemAdd = this.onItemAdd.bind(this);
}
onItemChange(event) {
this.setState({
item: {
id: uuid(),
[event.target.name]: event.target.value
}
});
}
onItemAdd(event) {
event.preventDefault();
const { item } = this.state;
this.setState({ listItems: [...this.state.listItems, item] });
document.getElementById('formItem').reset();
}
render() {
const currentItem = this.onItemChange;
const addItem = this.onItemAdd;
const listItems = this.state.listItems;
return (
<div className="box">
<Context.Provider
value={{ currentItem, addItem, listItems }}
>
<DisplayItems />
<ItemInput />
</Context.Provider>
</div>
);
}
}
ItemInput:
import React, { useContext } from "react";
import Context from "../../../context/Context";
export default function ItemInput() {
const { currentItem, addItem } = useContext(Context);
return (
<form className="item" id="formItem">
<input
onChange={currentItem}
type="text"
className="newInput"
name="toDo"
placeholder="New Task"
autoComplete="off"
/>
<button onClick={addItem} className="checkButton">
<i className="fas fa-check fa-sm"></i>
</button>
</form>
);
}
DisplayItems:
import React, { useContext } from "react";
import Context from "../../../context/Context";
export default function DisplayItems() {
const { listItems, removeItem } = useContext(Context);
return (
<div>
{listItems.map((item) =>
<div className="item" key={item.id} >
<input type="checkbox" />
<p className="listItem">{item}</p>
<button
className="delete-btn"
type="submit"
onClick={removeItem.bind(this, item.id)}
>
<i className="far fa-trash-alt fa-sm"></i>
</button>
</div>
)}
</div>
)
}
When im trying to add new object console gives me this error:
Uncaught Error: Objects are not valid as a React child (found: object
with keys {id, toDo}). If you meant to render a collection of
children, use an array instead.
As far as i understand there's something wrong in my Display Items rendering function, can you suggest whats wrong pls?
item is an object, that why react yelling the error at you. React cant render an object.
// You previously define item as an object: item: { id: '', toDo: ''}
// So the JSX below result in error
<p className="listItem">{item}</p>
Beside JSX, expressions inside render function should return a string (or can be convert to a string). So it should be something like
<p className="listItem">{item.toDo}</p>

React Component array not updating

I'm learning react, and I'm stuck with not updating list components.
The component shows all of the list elements that I add manually, but not rendering any changes.
I searched a lot for solutions.
All of my change handlers are binded, the setState inside handleSubmit should update ClockRow...
My App.js:
import React, { Component } from 'react';
import Clock from './Clock';
import ClockRow from './ClockRow';
class App extends Component {
constructor(props) {
super(props);
this.state = {items: [], tle: 'Teszt', ival: 200};
this.handleChangeTitle = this.handleChangeTitle.bind(this);
this.handleChangeInterval = this.handleChangeInterval.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChangeTitle(event) {
this.setState({tle: event.target.value});
}
handleChangeInterval(event) {
this.setState({ival: event.target.value});
}
handleSubmit(event) {
if(this.state.tle.length > 0 && this.state.ival > 9){
this.setState({items: [...this.state.items, <Clock interval={this.state.ival} title={this.state.tle} />]});
}
event.preventDefault();
}
render(){
return (
<div>
<div className="row">
<h1 className="col text-center">Hello, React!</h1>
</div>
<div className="row">
<form onSubmit={this.handleSubmit}>
<label>
Title: <input type="text" name="tle" value={this.state.tle} onChange={this.handleChangeTitle} />
</label>
<label>
Interval: <input type="number" name="ival" value={this.state.ival} onChange={this.handleChangeInterval} />
</label>
<input type="submit" value="Add" />
</form>
</div>
<ClockRow clockItems={this.state.items} />
</div>
);
}
}
export default App;
My ClockRow.js:
import React, { Component } from 'react';
class ClockRow extends Component{
constructor(props){
super(props);
this.state = {clocks: props.clockItems.map((x, i) => <div className="col" key={i}>{x}</div>) }
}
render(){
return(<div className="row">{this.state.clocks}</div>
)};
}
export default ClockRow;
My Clock.js:
import React, { Component } from 'react';
import {Card, CardTitle, CardBody, CardFooter} from 'reactstrap';
class Clock extends Component {
constructor(props){
super(props);
this.state = {counter: 0, interval: parseInt(props.interval), title: props.title};
}
componentDidMount() {
this.timerID = setInterval(() => this.tick(), this.state.interval);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState((state) => ({
counter: state.counter + 1
}));
}
render() {
return (
<Card>
<CardTitle>{this.state.title}</CardTitle>
<CardBody>{this.state.counter}</CardBody>
<CardFooter>{this.state.interval}</CardFooter>
</Card>
);
}
}
export default Clock;
CodeSandbox:
https://codesandbox.io/s/zxlzzv05n3
ClockRow.js is surplus
Clock.js is not changed
App.js is changed, and "React styled":
import React, { Component } from "react";
import Clock from "./Clock";
class App extends Component {
constructor(props) {
super(props);
this.state = { items: [], inputTle: "Teszt", inputIval: 200 };
}
handleChangeTitle = event => {
this.setState({ inputTle: event.target.value });
};
handleChangeInterval = event => {
this.setState({ inputIval: event.target.value });
};
handleSubmit = event => {
console.log(this.state);
if (this.state.inputTle.length > 0 && this.state.inputIval > 9) {
this.setState(prevState => {
return {
items: [
...prevState.items,
{
title: this.state.inputTle,
interval: this.state.inputIval
}
]
};
});
}
event.preventDefault();
};
render() {
return (
<div>
<div className="row">
<h1 className="col text-center">Hello, React!</h1>
</div>
<div className="row">
<form onSubmit={this.handleSubmit}>
<label>
Title:{" "}
<input
type="text"
name="tle"
value={this.state.inputTle}
onChange={this.handleChangeTitle}
/>
</label>
<label>
Interval:{" "}
<input
type="number"
name="ival"
value={this.state.inputIval}
onChange={this.handleChangeInterval}
/>
</label>
<input type="submit" value="Add" />
</form>
</div>
<div className="row">
{this.state.items.map((item, index) => (
<div className="col" key={index}>
<Clock {...item} />
</div>
))}
</div>
</div>
);
}
}
export default App;

How to display data from Mob-x store on click

I have this component:
import React, { Component } from 'react';
import { inject, observer } from 'mobx-react';
import PropTypes from 'prop-types';
import './cardCheck.css';
#inject('auto')
#observer
class CardCheck extends Component {
render() {
const { auto } = this.props;
return (
<div>
<div className="newsletter-container">
<h1>Enter the ID of your card:</h1>
<div className="center">
<input type="number" />
<input type="submit" value="Check" onClick={event => {displayInfo}} />
</div>
<div className="results"></div>
</div>
<h1>Offers:</h1>
</div>
);
}
}
CardCheck.propTypes = {
auto: PropTypes.shape({
carCount: PropTypes.number
})
};
CardCheck.wrappedComponent.defaultProps = {
auto: autoPropTypeDefaults
};
export default CardCheck;
I want to make so that when someone clicks the input with value Check the div below with the className of results to be populated with my data from the store:
import { observable, action, computed } from 'mobx';
class Auto {
#observable
auto = [
{
name: 'Shop1'
},
{
name: 'Shop2'
}
];
#action
displayInfo() {
}
}
export { Auto };
I tried to make the displayInfo function iterate through the array with objects with a map from loadash but it didn't work, it did not displayed the data inside the div, how can I do this?
You can add an observable field on your CardCheck component that you toggle with the onClick event handler, and when the field is true you can display all entries in the auto array.
Example
class CardCheck extends Component {
#observable
checked = false;
onClick = event => {
event.preventDefault();
this.checked = !this.checked;
};
render() {
const { auto } = this.props;
return (
<div>
<div className="newsletter-container">
<h1>Enter the ID of your card:</h1>
<div className="center">
<input type="number" />
<input type="submit" value="Check" onClick={this.onClick} />
</div>
<div className="results">
{this.checked &&
auto.auto.map((a, index) => <div key={index}>{a.name}</div>)}
</div>
</div>
<h1>Offers:</h1>
</div>
);
}
}

ReactJS: How do I add a new row to table from Form input values?

I'm learning ReactJS.
I'd like to add text into a input field and then click a submit button. this new value is add to a table.
I've started with a simple form component that has input values first name and last name. A table component that extends the form. the form added to the default App class.
Form.js
import React, { Component } from 'react';
import './form.css';
export default class Form extends Component {
render(){
return(
<div id="Form">
<form onSubmit={this.handleSubmit}>
Username<br/>
<input type="text" name="username" placeholder="Username" /><br/>
Password<br/>
<input type="password" name="password" placeholder="Password"/><br/>
<br/>
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
Table.js
import React, { Component } from 'react';
import './table.css';
import Form from './Form.js';
export default class Table extends Form {
render(){
return(
<div id="Table">
<table>
<tr>
<th>Username</th>
<th>Password</th>
</tr>
<tr>
<td>a</td>
<td></td>
</tr>
</table>
</div>
);
}
}
App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import Table from './Table.js';
import Form from './Form.js';
import './App.css';
class App extends Component {
render() {
return (
<div className="App">
<Form />
</div>
);
}
}
export default App;
I made a CodePen with a working example.
Explanation
You have to write the methods on your parent component and pass them as props to your child components. The state of your application will be on the parent component as well.
The methods you should create are the handleInputChange and handleFormSubmit. You have to pass both methods as props to your Form component.
And the state will have a 'username' and a 'password' for the new item, and an array of existing items and where the new items will be added. You will pass the array of items to your Table component as a prop.
In the Table component just map the items prop and print a <tr> with the data for each item in the array.
./App.js
import React, { Component } from 'react';
import './App.css';
import Table from './Table';
import Form from './Form';
class App extends Component {
constructor() {
super();
this.state = {
username: '',
password: '',
items: []
}
};
handleFormSubmit = (e) => {
e.preventDefault();
let items = [...this.state.items];
items.push({
username: this.state.username,
password: this.state.password
});
this.setState({
items,
username: '',
password: ''
});
};
handleInputChange = (e) => {
let input = e.target;
let name = e.target.name;
let value = input.value;
this.setState({
[name]: value
})
};
render() {
return (
<div className="App">
<Table items={ this.state.items }/>
<Form handleFormSubmit={ this.handleFormSubmit }
handleInputChange={ this.handleInputChange }
newUsername={ this.state.username }
newPassword={ this.state.password } />
</div>
);
}
}
export default App;
./Form.js
import React, { Component } from 'react';
class Form extends React.Component {
render() {
return (
<div id="Form">
<h3>Add a new item to the table:</h3>
<form onSubmit={this.props.handleFormSubmit}>
<label htmlFor="username">
Username:
<input id="username" value={this.props.newUsername}
type="text" name="username"
onChange={this.props.handleInputChange} />
</label>
<label for="password">
Password:
<input id="password" value={this.props.newPassword}
type="password" name="password"
onChange={this.props.handleInputChange} />
</label>
<button type="submit" value="Submit">Add Item</button>
</form>
</div>
);
}
}
export default Form;
./Table.js
import React, { Component } from 'react';
class Table extends React.Component {
render() {
const items = this.props.items;
return (
<div id="Table">
<table>
<tbody>
<tr>
<th>Username</th>
<th>Password</th>
</tr>
{items.map(item => {
return (
<tr>
<td>{item.username}</td>
<td>{item.password}</td>
</tr>
);
})}
</tbody>
</table>
</div>
);
}
}
export default Table;
I recommend you to read the Controlled Components section of the React documentation.
For more complex form cases you could use Redux and redux-form.
I hope this will help.
You can do like below. store your input field values to an array state and pass it to your Table component as props. In your Table component take an array variable and iterate your formData props then push username and password to rows array and print that.
Full functionality you can check below
Form.js
import React, { Component } from 'react';
import './form.css';
import Table './Table.js';
export default class Form extends Component {
constructor(props){
super(props);
this.state = {
uName: '',
uPassword: '',
formValid: false,
formData: []
}
this.changeUsername = this.changeUsername.bind(this);
this.changePassword = this.changePassword.bind(this);
}
changeUsername(event){
this.setState({
uName: event.target.value
})
}
changePassword(event){
this.setState({
uPassword: event.target.value
})
}
handleSubmit(e){
e.preventDefault();
if(this.state.uName != "" && this.state.uPassword != "" && this.state.uName != null && this.state.uPassword != null){
let object = {}
object.username = this.state.uName;
object.password = this.state.uPassword;
this.setState({
formValid: true,
formData: obj
})
}
}
renderTable(){
<Table formData = {this.state.formData} />
}
render(){
return(
<div id="Form">
<form onSubmit={this.handleSubmit}>
Username<br/>
<input type="text" value = {this.state.uName} name="username" placeholder="Username" onChange={this.changeUsername}/><br/>
Password<br/>
<input type="password" value = {this.state.uPassword} name="password" placeholder="Password" onChange={this.changePassword}/><br/>
<br/>
<input type="submit" value="Submit" />
</form>
{this.state.formValid ? this.renderTable() : ''}
</div>
);
}
}
Table.js
import React, { Component } from 'react';
import './table.css';
export default class Table extends Form {
constructor(props){
super(props);
}
render(){
const {formData} = this.props;
let rows = []
if(formData){
for(var i=0; i<formData.length;i++){
rows.push(<tr><td>{formData[i].username}</td><td>{formData[i].password}</td></tr>)
}
}
return(
<div id="Table">
<table>
<tr>
<th>Username</th>
<th>Password</th>
</tr>
{rows}
</table>
</div>
);
}
}
handSubmit =(e)=> {
for(let i=0; e.target.length-1>=i; i++){
console.log(e.target[i].value)
}
e.preventDefault()
}

How to change the form fields by clicking the button in REACT.js?

I have signup.jsx
import React from "react"
import { render } from "react-dom"
import SignupContainer from "./containers/SignupContainer"
class Signup extends React.Component {
user(){
this.props.type='user';
}
hotel(){
this.props.type='hotel';
}
render() {
return (
<div>
Registration Type :
<br></br>
<button onClick={this.user}>user</button>
<button onClick={this.hotel}>hotel</button>
<SignupContainer type={this.props.type}/>
<h1>Signup</h1>
</div>
);
}
}
render(<Signup type='user'/>, document.getElementById('Signup'))
My SignupContainer.jsx
import React from "react"
import Headline from "../components/Headline"
export default class SignupContainer extends React.Component {
render() {
if(this.props.type=='user'){
return (
<div className="container">
<div className="row">
<div className="col-sm-12">
<form action="/loginapp/" method="POST">
First Name:
<input type="text" name="first_name">
</input>
<br></br>
Last Name:
<input type="text" name="last_name"/>
<br></br>
Gender:
<input type="radio" name="gender" value="male" />Male
<input type="radio" name="gender" value="female" /> Female
<br></br>
<input type="submit" value="Submit"/>
</form>
</div>
</div>
</div>
);
} else if(this.props.type=='hotel'){
return(<h1>{this.props.type}</h1>);
}
}
}
What i want is that when i click on user button then it should show me the registration form and when i click on hotel button it should print hotel without reloading the page.
In React, props are passed down from parent to child, and should be considered immutable. On the other hand, state is used by components internally and can be updated with this.setState(), which triggers a re-render. Also, when using native JavaScript classes, you need to bind the class methods to the class if you want this to refer to class. So in your case, something like this should work:
class Signup extends React.Component {
constructor(props) {
super(props);
this.state = { // this is your default state
type: 'user'
}
}
user() {
this.setState({
type: 'user'
})
}
hotel() {
this.setState({
type: 'hotel'
})
}
render() {
return ( < div >
Registration Type:
< br > < /br>
<button onClick={this.user.bind(this)}>user</button >
<button onClick = {this.hotel.bind(this)}>hotel</button>
<SignupContainer type={this.state.type} />
<h1> Signup </h1>
</div>
);
}
}
render( < Signup type = 'user' / > , document.getElementById('Signup'))
Try not to change props of own component after component has mounted. Instead use states.
import React from "react"
import {render} from "react-dom"
import SignupContainer from "./containers/SignupContainer"
class Signup extends React.Component {
constructor(props){
super(props);
this.state = {type : this.props.type};
}
user() {
this.setState({type: 'user'});
}
hotel() {
this.setState({type: 'hotel'});
}
render() {
return ( <div >
Registration Type:
<br />
<button onClick={this.user}>user</button >
<button onClick = {
this.hotel
} > hotel </button>
<SignupContainer type={this.state.type}/ >
<h1> Signup </h1>
</div>
);
}
}
render( < Signup type = 'user' / > , document.getElementById('Signup'))

Resources