I am making a simple react test application.
What is the application: It shows names of projects and has an option to add a new project. Each project has title and category.
This is how it looks
Problem: When I try to add a new project by entering the title and then clicking on submit button, the new project name appears in the projects for a fraction of seconds and then disappears. The project list gets back to the initial state which is three projects (which are shown below)
This is the code
App.js
import React, { Component } from 'react';
import './App.css';
import Projects from "./Components/Projects"
import AddProjects from './Components/AddProject'
class App extends Component {
constructor(){
super();
this.state = {
projects:[]
}
}
componentWillMount(){
this.setState({projects:[
{
title: "Trigger",
category: "Web App"
},
{
title: "Trigger",
category: "Web App"
},
{
title: "Trigger",
category: "Web App"
}
]})
}
handleAddProject(project){
let projects = this.state.projects;
projects.push(project)
this.setState({projects:projects})
console.log(this.state.projects)
}
render() {
return (
<div className="App">
My Project
<AddProjects addProject={this.handleAddProject.bind(this)}/>
<Projects projects={this.state.projects}/>
</div>
);
}
}
export default App;
Projects.js
import React, { Component } from 'react';
import Projectitem from './ProjectItem'
class Projects extends Component {
render() {
let projectItems
if(this.props.projects){
projectItems = this.props.projects.map(project =>{
return (
<Projectitem project={project}/>
);
})
}
else{
console.log("hello")
}
return (
<div>
This is a list of objects
{projectItems}
</div>
);
}
}
export default Projects
ProjectItem.js
import React, { Component } from 'react';
class ProjectItem extends Component {
render() {
return (
<li>
{this.props.project.title}:{this.props.project.category}
</li>
);
}
}
export default ProjectItem
AddProject.js
import React, { Component } from 'react';
var categories = ["Web dev", "Mobile dev", "websiite"]
class AddProject extends Component {
constructor(){
super();
this.state = {
newProject:{}
}
}
handleSubmit(){
this.setState({newProject:{
title:this.refs.title.value,
category:this.refs.category.value
}}, function () {
this.props.addProject(this.state.newProject)
})
}
render() {
var categoryOptions = categories.map(category=>{
return <option key={category} value={category}>{category}</option>
})
return (
<div>
Add Project <br/>
<form onSubmit={this.handleSubmit.bind(this)}>
<div>
<label>title</label><br/>
<input type="text" ref="title"/>
</div>
<div>
<label>Category</label><br/>
<select ref="category">
{categoryOptions}
</select>
</div>
<input type="submit" value="Submit"/>
</form>
</div>
);
}
}
export default AddProject
Add event.preventDefault() to your handleSubmit() method
handleSubmit(event) {
event.preventDefault();
alert('A name was submitted: ' + this.state.value);
}
Without it your page will refresh (as it does by default when a form is submitted)
You are editting the state directly, that's a big no-no in React.
Change your handler function to something like this:
handleAddProject(project){
this.setState((prevState) => {
return { projects: [...prevState.projects, project] }
})
}
The setState function can be called using a 'callback', that receives the state previous to its call, and lets you modify it. The important part is not mutating the state directly (as your projects.push(project) line is doing).
Other option, that's more like what you are already trying to do, is copying the current state before mutating it:
handleAddProject(project){
let projects = this.state.projects.slice(); // notice the slice here will return
// a copy of 'projects', and then
// you modify it
projects.push(project)
this.setState({projects:projects})
console.log(this.state.projects)
}
Also, keep in mind, setState is called asynchronously, so the console.log(this.state.projects) call may show the old state yet, as setState may not have been called yet.
You are using a Form tag which is for submit information to a web server if you use the input type="submit" it will always reload the page and initial everithig as the first time loaded. If you do as #Galupuf it will work
because the event.preventDefault(); will not allow the browser to reload and it will not initial everyting as first time loaded.
My answer is if you are not sending any data to any sever or working with a server at all change the form tag for div the input type="submit" for<button onClick={()=>this.handleSubmit()}>Send</button>
<div>
<div>
<label>title</label><br/>
<input type="text" ref="title"/>
</div>
<div>
<label>Category</label><br/>
<select ref="category">
{categoryOptions}
</select>
</div>
<button onClick={()=>this.handleSubmit()}>Submit</button>
</div>
other suggestion work the modified value of the state like #cfraser suggest like that you will have immutable data
Related
I am trying to have a controlled input set up in a child component (the Search component). I wanted to keep the input state in the main App component so that I can access it in my apiCall method. I am getting the following error:
Warning: You provided a value prop to a form field without an onChange handler. This will render a read-only field. If the field should be mutable use defaultValue. Otherwise, set either onChange or readOnly.
However, I did add an onChange handler. I'm assuming the problem is that the onChange handler function is in the parent component and React doesn't like this. I did try moving the input to the main App component and worked fine (logged input to console).
Am I going about this wrong? And is there a way to set it up so that I can access the input from the Search component in the App component? I was hoping to keep most of my code/functions/state in the main App component.
Here is the App component
import React, { Component } from "react";
import './App.css';
import Header from './Components/Header'
import Search from './Components/Search'
import MainInfo from './Components/MainInfo'
import Details from './Components/Details'
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
weather: null,
main: '',
wind: '',
loading: null,
cityInput: 'Houston',
city: 'City Name',
date: new Date()
};
this.apiCall = this.apiCall.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
cityInput: event.target.value
})
console.log(this.state.cityInput)
}
// Fetch data from OpenWeatherAPI
apiCall() {
this.setState({
loading: true
})
const currentWeather = fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${this.state.cityInput}&appid={apiKey}&units=imperial`
).then((res) => res.json());
const futureWeather = fetch(
`https://api.openweathermap.org/data/2.5/forecast?q=houston&appid={apiKey}&units=imperial`
).then((res) => res.json());
const allData = Promise.all([currentWeather, futureWeather]);
// attach then() handler to the allData Promise
allData.then((res) => {
this.setState({
weather: res[0].weather,
main: res[0].main,
wind: res[0].wind,
city: res[0].name
})
});
}
componentDidMount() {
this.apiCall();
}
render() {
return (
<div className="container-fluid bg-primary vh-100 vw-100 d-flex flex-column align-items-center justify-content-around p-3">
<Header />
<Search cityInput={this.state.cityInput} />
<MainInfo main={this.state.main} date={this.state.date} city={this.state.city} weather={this.state.weather} />
<Details main={this.state.main} wind={this.state.wind} />
</div>
);
}
}
export default App;
Here is Search component
import React, { Component } from "react";
class Search extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="row">
<div className="col-12">
<div className="d-flex">
<input className="form-control shadow-none mx-1" placeholder="Enter a city..." value={this.props.cityInput} onChange={this.handleChange}></input>
<button className="btn btn-light shadow-none mx-1" onClick={this.apiCall}>Test</button></div>
</div>
</div>
);
}
}
export default Search;
The Search component is indeed unaware of the implementation of the onChange function you have made in your App. If you really want to use a function from the parent (App) component in the child (Search), you'll need to add it as a property, as such:
<Search cityInput={this.state.cityInput} onChange={this.onChange} />
Then, you need to set it in the Child component's constructor:
constructor(props) {
super(props);
this.onChange = props.onChange;
}
I also suggest you'll have a look at React's functional approach with hooks https://reactjs.org/docs/hooks-intro.html, which makes all this a whole lot less fiddly, in my opinion. But it might take a bit to get used to.
u can pass functions like ur handler over the prop to childrens and update so from a child to the mother of the children, in the children u give the value the prop u supply from mother
<Select dataFeld="broker" id="yourid" value={this.state.brokerSel} ownonChange={(e) => this.setState({statename: e})
I wanted to check how to react does reconciliation so I updated the inner HTML of id with the same text. Ideally, it shouldn't update the dom but it is paint reflashing in chrome.
I have tried paint reflashing in chrome it is showing green rectangle over that same text
import React from 'react';
function App() {
return (
<div >
<p id="abc" key="help">abc is here</p>
<button onClick={function () {
// document.getElementById("may").innerHTML = "";
document.getElementById("abc").innerHTML = "abc is here";
}} > Btn</button>
</div>
);
}
export default App;
Expected result should be that paint reflashing shouldn't happen but it is happening.
You are not using React here to update the text of your p tag but directly updating the DOM with JavaScript.
So React reconciliation algorithm doesn't even run here.
In React, the output HTML is a result of the state and the props of your component.
When a change in state or props is detected, React runs the render method to check if it needs to update the DOM. So, in order to do this check, you need to store the parameters that determine your view in state or props.
Given your example, we could save the text you want to show in the p tag in the state of your component (using hooks):
import React, { useState } from 'react';
function App () {
const [text, setText] = useState('abc is here');
render() {
return (
<div >
<p id="abc" key="help">{this.state.text}</p>
<button onClick={() => setText('abc is here') }>Btn</button>
</div>
);
}
}
export default App;
If you are using a version of React that does not support hooks, you will need to transform your functional component into a class to use state:
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = { text: 'abc is here' };
}
render() {
return (
<div >
<p id="abc" key="help">{this.state.text}</p>
<button onClick={() => this.setState({ text: 'abc is here' }) }>Btn</button>
</div>
);
}
}
export default App;
I am trying to create a multiple page form in React, and I have the basic wireframe set up. I am trying to export a user's Name from one page to the next, but the user will change depending on who has logged in. I've been in google purgatory for a while trying figure out how to grab a specific state-based value out of a component to be available on another page. In my code below, I'm exporting the whole component to render on the App.js page. However, I'd also like to grab just the {userName} to render within another component.
import React, { Component } from 'react'
class Intro extends Component {
state = { userName: ''}
handleChange = (event) => this.setState({ userName: event.target.value })
render() {
const { userName } = this.state
return (
<div id='intro'>
<form>
<FieldGroup
id='nameArea'
value={this.state.value}
onChange={this.handleChange}
/>
<input id='submit' type='submit' value='Submit' /> .
</form>
</div>
)
}
}
export default Intro
To put it simply, you can't. This is where tools like redux come into play. Here's an example using React's new context API:
const UserContext = React.createContext('');
class Intro extends Component {
handleChange = (event) => {
this.props.updateUserName(event.target.value);
}
render() {
const { userName } = this.props
return (
<div id='intro'>
<form>
<input
id='nameArea'
value={userName}
onChange={this.handleChange}
/>
<input id='submit' type='submit' value='Submit' /> .
</form>
</div>
)
}
}
// only doing this to shield end-users from the
// implementation detail of "context"
const UserConsumer = UserContext.Consumer
class App extends React.Component {
state = { userName: '' }
render() {
return (
<UserContext.Provider value={this.state.userName}>
<React.Fragment>
<Intro userName={this.state.userName} updateUserName={(userName) => this.setState({userName})} />
<UserConsumer>
{user => <div>Username: {JSON.stringify(user)}</div>}
</UserConsumer>
</React.Fragment>
</UserContext.Provider>
)
}
}
See my updated codesandbox here.
Most of the time, when you need data on another component, the solution is store the date higher in your component.
As the others already said, the most easy way is trying to pass your state via props from your higher order component to the childs.
Another approach would be to use Redux for your state management. This gives you one global state store accessible from any component.
Third you can try to use the react context api.
I'm a newbie in React. Why can't my data pass to another component?? My idea is after the user select option tag, then it will load some audio files based off the selection. But it's not working. What did I miss?
Or is there a better to do it?
This is my dropdown component
import React from 'react';
import Playlist from './Playlist/Playlist.js';
class Dropdown extends React.Component {
constructor (props) {
super(props);
this.state = {value: "1"};
this.handleChange = this.handleChange.bind(this);
this.handleEnter = this.handleEnter.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
console.log("Hello");
console.log(event.target.value);
console.log(typeof(event.target.value));
}
handleEnter(event) {
alert("Clicked: " + this.state.value);
event.preventDefault();
}
render (){
return (
<form onSubmit={this.handleEnter} >
<label>
<div className="langueageSelection">
<Playlist langueageSelection={this.handleChange.bind(this)} />
<select value={this.state.value} onChange={this.handleChange} >
<option value=""> Please select a language </option>
<option value="1"> English (American) </option>
<option value="2"> Chinese (Mandarin) </option>
</select>
</div>
</label>
<input type="submit" value="Enter" className="EnterButton"/>
</form>
);
}
}
export default Dropdown;
This is my playlist component.
import React from 'react';
class Playlist extends React.Component {
handleChange({target}){
this.props.langueageSelection(target.value)
}
render() {
if (this.props.langueageSelection === "1"){
console.log("English");
}
else if(this.props.langueageSelection === "2"){
console.log("Chinese");
}
return (
<div> Hi</div>
);
}
}
export default Playlist;
This is my entry component:
import React from 'react';
import mind_zebra from '../../images/MindScribe-zebra.png';
import Dropdown from '../Dropdown/Dropdown.js';
class Entry extends React.Component {
state = { hideZebraPic : false};
onClickHandler = () => {
this.setState( prev => ({ hideZebraPic : !prev.hideZebraPic }));
};
render() {
if (this.state.hideZebraPic) {
return (
<div>
<Dropdown />
</div>
);
} else {
return (
<div>
<img src={mind_zebra} onClick={this.onClickHandler} className="MindZebraPic" alt="zebra"/>
</div>
);
}
}
}
Here's my structure of directory:
src
|
audio
- English audios
- Chinese audios
components
|
Dropdown
|
playlist
- playlist.js
dropdown.js
|
Entry
|
entry.js
|
Home
|
App.js
Or is there a better to do it?
Please take a look Single source of truth and its implementation by Redux or MobX
Maybe could you please specify what exactly does not work? Playlist does not receive selected language? Should it?
Because now, it looks like this:
<Playlist langueageSelection={this.handleChange.bind(this)} />
You pass function to Playlist, right? And as I understand, role of Playlist is to render some content, based on selections made in parent component, which is Dropdown? Why would you pass a function to the children component? An why would you decorate it with .bind(this)? You've bound "it" already in constructor (which is a "Dropdown" as I understand).
If you really need to get it working as it is:
In Dropdown, pass the "value" as a prop to child
<Playlist langueageSelection={this.state.value} />
In Playlist, I'm not quite sure if you need this function to be passed.
import React from 'react';
class Playlist extends React.Component {
render() {
if (this.props.langueageSelection === "1"){
console.log("English");
}
else if(this.props.langueageSelection === "2"){
console.log("Chinese");
}
return (
<div> Hi</div>
);
}
}
export default Playlist;
For further development:
I think you could get in touch with some programming patterns of React. Please make some research on "stateless components updating parent's state". I don't know your idea about whole application, but I feel that Playlist should not be a child of a Dropdown. I would propose something like this:
Dropdown and Playlist are siblings. Let's say that they parent App. App is stateful component. Dropdown and Playlist are stateless components. App passes function ChangeLanguage to the Dropdown. ChangeLanguage is bound to App. Function ChangeLanguage handles selection of lang in Dropdown, takes event and updates App state with event.target.value. Then App passes its state to Playlist as a prop.
I hope that I understood your issue right.
I am pretty new to React JS and I am just wondering how I can filter what my component renders based on the state of my prop.
So I have the following component that allows me to select a certain brand and store it as a prop:
var React = require('react');
import Select from 'react-select';
class VehicleSelect extends React.Component {
constructor(props) {
super(props);
this.state = { brandSelect: ""};
}
_onChange(value) {
//console.log(value) - just to see what we recive from <Select />
this.setState({brandSelect: value}, () => {
console.log(this.state.brandSelect);
});
}
render() {
var options = [
{ value: 'Volkswagen', label: 'Volkswagen' },
{ value: 'SEAT', label: 'SEAT' },
{ value: 'SKODA', label: 'SKODA' }
];
return (
<Select
name="form-field-name"
value={this.state.brandSelect}
options={options}
placeholder="Select a brand"
searchable={false}
onChange={this._onChange.bind(this)}
/>
)
}
};
// Export our component
export default VehicleSelect;
This component works as expected however I am having issues when it comes to taking the brandSelect prop and conditionally deciding what my component should render.
Here is my details component:
var React = require('react');
import { If, Then, Else } from 'react-if';
import VehicleSelect from './vehicle-select.js';
// Vehicle Description Component
const VehicleDetail = (props) => {
return (
<If condition={ this.state.brandSelect === props.vehicle.brand.name }>
<div className="col-flex-md-3 col-flex-sm-4 col-flex-xs-6 col-flex-media-query">
<div className="vehicle-container">
<img src={"https://s3-eu-west-1.amazonaws.com/pulman-vw-images/uploads/images/thumbnails/" + props.vehicle.offers[0].image.name} />
<h4 className="vehicle-title">
{props.vehicle.model.name}
</h4>
<div className="row-flex">
<div className="col-flex-xs-12 btn-container">
Learn More
</div>
</div>
</div>
</div>
</If>
);
};
// Export our component
export default VehicleDetail;
As you can see it constructs a HTML container with data. I have also added a conditional statement (react-if on GitHub) to try and render data that matches the option that was selected in my VehicleSelect component however this doesn't seem to work. I get the following error:
Warning: There is an internal error in the React performance measurement code. Did not expect componentDidUpdate timer to start while render timer is still in progress for another instance.
Here is my component that iterates over my VehicleDetail component and supplies data to it:
var React = require('react');
// Vehicle List Componen
import VehicleDetail from './vehicle-detail.js';
// Create our component
const VehicleList = (props) => {
// Just add props.vehicle to access API data instead of static
const RenderedVehicles = props.vehicles.map(vehicle =>
<VehicleDetail key={vehicle.slug} vehicle={vehicle} />
);
return (
<div className="row-flex center-xs">
{RenderedVehicles}
</div>
);
};
// Export our component
export default VehicleList;
So my question is where am I going wrong? As I am new to ReactJS I am unsure how I can render components based on the state that has been selected. All I am trying to do is show data that matches the brandSelect prop in my VehicleSelect component. If the brandSelect prop is equals "" then I would like to render all of the data that is mapped in my VehicleList component.
Thanks
You cannot use this.state.brandSelect in the Details component
Since your details component is setup and brandselect is present in the parent component too