Trying to learn React/RoR and in this simple test app I have a 'searchapp' react app. That sets a default for the language radioboxes.
import React from 'react'
import ReactDOM from 'react-dom'
import axios from "axios";
import SearchForm from "./searchForm";
class SearchApp extends React.Component {
constructor(props) {
super(props);
this.state = {
searchStrings: [],
subjectName: "",
language: 'English',
region: ""
};
this.getSearchStrings = this.getSearchStrings.bind(this);
}
componentDidMount() {
this.getSearchStrings();
}
handleClickLang = changeEvent => {
this.setState({
language: changeEvent.target.value
});
};
getSearchStrings() {
axios
.get("/api/v1/search_strings")
.then(response => {
const searchStrings = response.data;
this.setState({searchStrings});
})
.catch(error => {
console.log(error);
});
}
render() {
return (
<>
<SearchForm />
</>
);
}
}
and then in the searchForm component I use that state to set and switch between two radio buttons.
class SearchForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.subjRef = React.createRef();
//this.handleClickLang = this.handleClickLang.bind(this);
}
componentDidMount() {
this.setState({
language: 'English'
});
}
handleSubmit(e) {
e.preventDefault();
window.alert("sometext");
}
render() {
return (
<form onSubmit={this.handleSubmit} className="my-3">
<div className="form-row">
<div className="form-group col-md-8">
<p>Choose language</p>
</div>
<div className="form-row">
<div className="form-check">
<label>
<input
type="radio"
name="react-tips"
value="English"
checked={this.state.language === 'English'}
onChange={this.handleClickLang}
className="form-check-input"
/>
English
</label>
</div>
<div className="form-check">
<label>
<input
type="radio"
name="react-tips"
value="Russian"
checked={this.state.language === 'Russian'}
onChange={this.handleClickLang}
className="form-check-input"
/>
Russian
</label>
</div>
However when I run this I get the following error:
Uncaught TypeError: Cannot read properties of null (reading 'language')
I thought this was erroring because it cannot find a default language, however I initialised language to 'English' in the constructor for the SearchApp.
Is this me not understanding React state enough? Any help much appreciated.
In the componentDidMount i feel the language is getting to null, you can try the following:
Trying to learn React/RoR and in this simple test app I have a 'searchapp' react app. That sets a default for the language radioboxes.
import React from 'react'
import ReactDOM from 'react-dom'
import axios from "axios";
import SearchForm from "./searchForm";
class SearchApp extends React.Component {
constructor(props) {
super(props);
this.state = {
searchStrings: [],
subjectName: "",
language: 'English',
region: ""
};
this.getSearchStrings = this.getSearchStrings.bind(this);
}
componentDidMount() {
this.getSearchStrings();
}
handleClickLang = changeEvent => {
this.setState({
language: changeEvent.target.value
});
};
getSearchStrings() {
axios
.get("/api/v1/search_strings")
.then(response => {
const searchStrings = response.data;
this.setState({searchStrings,language: 'English'});
})
.catch(error => {
console.log(error);
});
}
render() {
return (
<>
<SearchForm language={this.state.language}/>
</>
);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
And in the SearchForm component pass the language via props
class SearchForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.subjRef = React.createRef();
//this.handleClickLang = this.handleClickLang.bind(this);
}
handleSubmit(e) {
e.preventDefault();
window.alert("sometext");
}
render() {
return (
<form onSubmit={this.handleSubmit} className="my-3">
<div className="form-row">
<div className="form-group col-md-8">
<p>Choose language</p>
</div>
<div className="form-row">
<div className="form-check">
<label>
<input
type="radio"
name="react-tips"
value="English"
checked={this.props.language === 'English'}
onChange={this.handleClickLang}
className="form-check-input"
/>
English
</label>
</div>
<div className="form-check">
<label>
<input
type="radio"
name="react-tips"
value="Russian"
checked={this.props.language === 'Russian'}
onChange={this.handleClickLang}
className="form-check-input"
/>
Russian
</label>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
But there are several more things:
Why are you also having language state in searchForm, you can have it in Search component and pass it via props
Yo are not initializing any state in the constructor in your SearchForm component
Adding a followup here for future reference.
As suggested by #Thor84no the problem was not adding state = {} to intialise state in the sub component.
Related
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;
This is my SearchForm.js class
import React from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
class SearchForm extends React.Component {
constructor(props) {
super(props)
this.state = {
position: '',
area: '',
date: '',
experience: {
type: Array,
default: function () { return [] }
}
}
this.handlePositionChange = this.handlePositionChange.bind(this);
this.handlePositionKeyUp = this.handlePositionKeyUp.bind(this);
this.handleAreaChange = this.handleAreaChange.bind(this);
}
...
render() {
return (
<form className='form search-form' onSubmit={this.handleSubmit}>
<div className="form-group col-md-2">
<label htmlFor="experience">Experience</label>
<select className="form-control" name="experience" id="experience" onChange={this.handleChange} value={this.state.experience}>
<option key={this.props.experience.id} value={this.props.experience.name}>
{this.props.experience.name}
</option>
</select>
</div>
</form>
)
}
}
export { SearchForm }
experience must be multiple array with fields id and name and I need to get its values from other server before rendering form and generate <option> tags for a <select> element previously. But I don't know how to make it. I use axios to send AJAX request on other inputs changes, so maybe I could use it here on window load or something like that?
the componentDidMount lifecycle function is where you need to make you API request. componentDidMount is called once after the initial render
class SearchForm extends React.Component {
constructor(props) {
super(props)
this.state = {
position: '',
area: '',
date: '',
experience: {
type: Array,
default: function () { return [] }
}
}
this.handlePositionChange = this.handlePositionChange.bind(this);
this.handlePositionKeyUp = this.handlePositionKeyUp.bind(this);
this.handleAreaChange = this.handleAreaChange.bind(this);
}
componentDidMount() {
//API request here
APICall().then(res => {
this.setState({data: res});
})
}
...
render() {
//conditional rendering here
if(!this.state.data) {
return 'Loading...'
}
return (
<form className='form search-form' onSubmit={this.handleSubmit}>
<div className="form-group col-md-2">
<label htmlFor="experience">Experience</label>
<select className="form-control" name="experience" id="experience" onChange={this.handleChange} value={this.state.experience}>
<option key={this.props.experience.id} value={this.props.experience.name}>
{this.props.experience.name}
</option>
</select>
</div>
</form>
)
}
}
export { SearchForm }
Hello everybody I'm wondering if I can pass a state value from a component to other where I'm returning jsx code to be displayed for example I have 3 components.
1
import React, { Component } from 'react';
import Conteneur from './Conteneur';
class Header extends React.Component {
constructor(props) {
super(props);
this.state = { value: '' };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ value: event.target.value });
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
<Conteneur values={this.state.value} />
</form>
);
}
}
export default Header;
2 app.js
import React, { Component } from 'react';
import Header from './Header';
import Conteneur from './Conteneur';
import './App.css';
class App extends Component {
render() {
return (
<div className="App" >
<br />
<Header />
<br />
<Conteneur />
</div>
);
}
}
export default App;
3 and finally
import React, { Component } from 'react';
const Conteneur = () => {
return (
<div className="tab"><span>ok test </span></div>
);
};
export default Conteneur;
I like to pass the state value of header that I have from the input to conteneur and then display in the box while I have some code all the examples that I saw online they are sending state like this:
class Dashboard extends Component {
...
...
render(){
return(
<Sidebar data={this.state.data1}/>
);
}
}
So can I do like this <Conteneur values={this.state.value} /> in the form ?
And I imported Conteneur.
i updated the code but the output is
Yes you can do, only one thing you are missing. Receive the props in the function parameters then render that in the ui.
Like this:
const Conteneur = (props) => {
return (
<div className="tab"><span>value: {props.value} </span></div>
);
};
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,
};
I am trying to pass data from one component to another. but it has no parent child relation and it is independent from each other. I want to do it using flux not redux. Can anyone help me to do this? below are my code.
export class EmpSearch extends React.Component {
constructor(props) {
super(props);
this.state = {
Empnumber: ''
};
}
updateEmpNumber(e) {
this.setState({Empnumber: e.target.value});
}
render() {
return (
<div className="row">
<form>
<div className="form-group">
<label htmlFor="Empnumber">Emp Number</label>
<input type="text" className="form-control" id="Empnumber" placeholder="Emp Number" value={this.state.Empnumber} onChange={this.updateEmpNumber.bind(this)}/>
</div>
</form>
</div>
);
}
}
export default EmpSearch
The other file is where i want to send the EmpNumber is below,
class EmpDetail extends React.Component {
render() {
return (
<div className="container">
<input type="text"/>
</div>
);
}
}
export default EmpDetail;
Assuming you have already implemented the flux architecture in your app.
your 1st component will be like this.
import React from 'react';
import UserAction from '../../Actions/UserActions';
export class EmpSearch extends React.Component {
constructor(props) {
super(props);
this.state = {
Empnumber: ''
};
}
updateEmpNumber(e) {
this.setState({Empnumber: e.target.value});
UserAction.employeeNumb(this.state.Empnumber);
}
render() {
return (
<div className="row">
<form>
<div className="form-group">
<label htmlFor="Empnumber">Emp Number</label>
<input type="text" className="form-control" id="Empnumber" placeholder="Emp Number" value={this.state.Empnumber} onChange={this.updateEmpNumber.bind(this)}/>
</div>
</form>
</div>
);
}
}
export default EmpSearch
The Actions file will look like
import {dispatch,register} from '../Dispatcher/Dispatcher';
export default {
employeeNumb(Data){
dispatch({ actionType:'EMPNO',data:Data});
}
}
The Store will look like
import {dispatch,register} from '../Dispatcher/Dispatcher';
import AppConstants from '../Constants/AppConstants';
import {EventEmitter} from 'events';
const CHANGE_EVENT = 'change';
var a=0;
const UserStore = Object.assign(EventEmitter.prototype,{
emitChange(){
this.emit(CHANGE_EVENT)
},
addChangeListener(callback){
this.on(CHANGE_EVENT,callback);
},
removeChangeListener(callback){
this.removeListener(CHANGE_EVENT,callback)
},
setEmpData(data){
a=data;
},
getEmpData(){
return a;
}
});
dispatcherIndex:register((action)=>{
switch (action.actionType) {
case AppConstants.EMPNO:
UserStore.setEmpData(action.data);
UserStore.emitChange();
break;
}
UserStore.emitChange();
});
export default UserStore;
The dispatcher file
import {Dispatcher} from 'flux';
const flux = new Dispatcher();
export function register(callback){
return flux.register(callback);
}
export function dispatch(actionType,action){
flux.dispatch(actionType,action);
}
and the 2nd Component file looks like
import React from 'react';
import Store from '../../Store/UserStore';
class EmpDetail extends React.Component {
constructor(props){
super(props);
this.state={
empno:''
};
}
componentDidMount(){
Store.addChangeListener(this._onChange);
}
componentWillUnmount = () =>{
Store.removeChangeListener(this._onChange);
}
_onChange = () =>{
this.setState({empno:Store.getEmpData()});
}
render() {
return (
<div className="container">
<input type="text"/>
<input type="button" onClick={()=>{console.log(this.state.empno);}}/>
</div>
);
}
}
export default EmpDetail;
What you have tried might be slightly different but this is the normal flow for what you are looking for.