I'm trying to pass value from one component to another. First one looks like this:
class ListStation extends Component {
constructor(props) {
super(props)
this.state = {
stations: []
}
this.editStation = this.editStation.bind(this);
}
editStation(id) {
this.props.history.push(`/add-station/${id}`);
}
componentDidMount() {
StationService.getStations().then((res) => {
this.setState({ stations: res.data });
})
}
}
render() {
return (
<div>
<tbody>
{this.state.stations.map(
station =>
<tr key={station.id}>
<td>{station.city}</td>
<td>{station.name}</td>
<td>
<button onClick={() => this.editStation(station.id)} className="btn btn-info">Modify</button>
...
</div>
</div>
);
}
}
export default ListStation;
And another looks like this:
import React, { Component } from 'react';
import StationService from '../services/StationService';
class CreateStationComponent extends Component {
constructor(props) {
super(props)
this.state = {
station: {
id: this.props.match.params.id,
city: '',
name: '',
trains: [
{
number: '',
numberOfCarriages: ''
}
]
}
}
this.changeCityHandles = this.changeCityHandles.bind(this);
this.changeNameHandles = this.changeNameHandles.bind(this);
this.saveStation = this.saveStation.bind(this);
}
componentDidMount() {
if (this.state.station[0].id === '_add') {
return;
} else {
StationService.getStationById(this.state.id).then((res) => {
let station = res.data;
this.setState({ name: station[0].name, city: station[0].city })
});
}
console.log(this.state.station.city + 'dfddddd');
}
But when I try to pass value from one component to another I get error: Property of undefined. The response I get from API looks like this:
I'm trying to edit values based on the id taken from the first component but it seems to fail.
if (this.state.station[0].id === '_add') {
return;
}
Have a look at this if statement from your codebase I think you should remove [0] after this.state.station ... this is because station is an object not an Array
Change it to if (this.state.station.id === '_add') {
Related
I'm trying to pass value from one component to another but when I do that, I get route in my http address as undefined instead of value. I have response from server in this form:
I'm trying to pass id values, and based on them make some actions. I get the error that GET request cannot be done due to value undefined.
Here is my code:
class StationService {
getStationById(id) {
return axios.get(STATION_API + '/station/' + id);
}
updateStation(station, id) {
return axios.put(STATION_API + '/station/' + id, station);
}
}
import React, { Component } from 'react';
import StationService from '../services/StationService';
class CreateStationComponent extends Component {
constructor(props) {
super(props)
this.state = {
station: {
id: this.props.match.params.id,
city: '',
name: '',
trains: [
{
number: '',
numberOfCarriages: ''
}
]
}
}
}
componentDidMount() {
if (this.state.station.id === '_add') {
return;
} else {
StationService.getStationById(this.state.id).then((res) => {
let station = res.data;
this.setState({ name: this.state.station[0].name, city: station[0].city })
});
}
console.log(this.state.station.name + 'dfddddd');
}
saveStation = (e) => {
e.preventDefault();
let station = { city: this.state[0].city, name: this.state[0].name }
if (this.state.id === '_add') {
StationService.createStation(station).then(res => {
this.props.history.push('/stations');
});
}
}
}
}
render() {
return (
<div>
...
</div >
);
}
}
From this component I want to pass id value to CreateStationComponent. But I don't know what I'm doing wrong.
import React, { Component } from 'react';
import StationService from '../services/StationService';
class ListStation extends Component {
constructor(props) {
super(props)
this.state = {
stations: []
}
this.addStation = this.addStation.bind(this);
this.editStation = this.editStation.bind(this);
this.deleteStation = this.deleteStation.bind(this);
this.showTrains = this.showTrains.bind(this);
}
deleteStation(id) {
StationService.deleteStation(id).then(res => {
this.setState({ stations: this.state.stations.filter(station => station.id !== id) });
})
}
editStation(id) {
this.props.history.push(`/add-station/${id}`);
}
componentDidMount() {
StationService.getStations().then((res) => {
this.setState({ stations: res.data });
})
}
render() {
return (
<div>
</div>
<div className="row">
<tbody>
{this.state.stations.map(
station =>
<tr key={station.id}>
<td>{station.city}</td>
<td>{station.name}</td>
<td>
<button onClick={() => this.editStation(station.id)} className="btn btn-info">Modify</button>
</tr>
)}
</tbody>
</table>
</div>
</div>
);
}
}
Any help would be appreciated.
Inside the constructor this.prop doesn't exist yet. Just access props.
constructor(props) {
super(props)
this.state = {
station: {
id: props.match.params.id,
city: '',
name: '',
trains: [
{
number: '',
numberOfCarriages: ''
}
]
}
}
}
Also pointed out in a comment, this.state.id isn't defined
StationService.getStationById(this.state.id)
but this.state.station.id is. Change the reference.
StationService.getStationById(this.state.station.id)
Since this.state.station is an object and not an array, this.setState({ name: this.state.station[0].name, city: station[0].city }) is also incorrect. this.state.station[0] is undefined and should throw error when attempting to access name. Update the reference.
this.setState({
name: this.state.station.name,
city: station[0].city,
})
And same for saveStation, update the state references.
saveStation = (e) => {
e.preventDefault();
let station = {
city: this.state.station.city,
name: this.state.station.name }
if (this.state.station.id === '_add') {
StationService.createStation(station).then(res => {
this.props.history.push('/stations');
});
}
}
I have a problem getting state data in the render function.
It works fine and returns the array when I use
console.log(items)
But trying to get first item from the array yields an error
console.log(items[0])
Full code is:
import React from "react";
import StatsSection from "./../components/StatsSection";
import { db } from "./../util/database";
class IndexPage extends React.Component {
constructor(props) {
console.log(props)
super(props);
this.state = {};
}
componentDidMount() {
var data = []
db.collection('test')
.get().then(snapshot => {
snapshot.forEach(doc => {
data.push(doc.data())
})
})
this.setState({
items: data
});
}
render() {
const { items } = this.state
console.log(items[0])
return (
<StatsSection
color="white"
size="medium"
backgroundImage=""
backgroundImageOpacity={1}
items={[
{
title: "Following",
stat: "123"
},
{
title: "Followers",
stat: "456k"
},
{
title: "Likes",
stat: "789"
}
]}
/>
);
}
}
export default IndexPage;
Where am I making the mistake?
You're only setting one item, so items is actually just one item and items[0] fails.
this.setState({
items: data
});
should be inside the .then() so that it only runs after all the items are populated with the .forEach().
Update your componentDidMount() like so:
componentDidMount() {
db.collection('test').get().then(snapshot => {
var data = []
snapshot.forEach(doc => {
data.push(doc.data())
})
this.setState({ items: data })
})
}
I have created three react components and I don't know why I am getting an infinite network request and this warning: index.js:1375 Warning: Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state.
in MenuCategory (at App.js:19)
in App (at src/index.js:5)
also a network request in MenuItems.js is getting called in a loop. I think it is due to setState but I don't know where is the error.
And here is my code :
import React from "react";
import MenuCategory from "./components/MenuCategory";
import MenuItems from "./components/MenuItems";
class App extends React.Component {
constructor(props) {
super(props);
this.state = { shortName: "" };
}
handleProps = ss => {
if (this.state.shortName === "") {
this.setState({ shortName: ss });
}
// console.log(ss, ".../PP");
};
render() {
return (
<div className="App">
<MenuCategory callback={this.handleProps} />
<MenuItems shortNameProp={this.state.shortName} />
</div>
);
}
}
export default App;
import React from "react";
class MenuCategory extends React.Component {
constructor(props) {
super(props);
this.state = { category: "", selectedCat: "" };
}
async UNSAFE_componentWillMount() {
const url = "http://stream-restaurant-menu-svc.herokuapp.com/category";
await fetch(url)
.then(data => data.json())
.then(element => {
this.setState({ category: element });
});
}
menuCat = () => {
let cat = this.state.category;
// console.log(cat, "...Cat", this.state.selectedCat, "...Cat");
if (this.state.selectedCat !== "") {
this.props.callback(this.state.selectedCat);
}
return cat.map(items => {
return (
<li
key={items.short_name}
onClick={() => this.setState({ selectedCat: items.short_name })}
>
{items.name}
</li>
);
});
};
render() {
return <div>{this.state.category.length > 0 ? this.menuCat() : null}</div>;
}
}
export default MenuCategory;
import React from "react";
class MenuItems extends React.Component {
constructor(props) {
super(props);
this.state = { catItems: "", items: "" };
}
renderItems = () => {
let shortName = this.props.shortNameProp;
if (shortName !== "") {
const url =
"https://stream-restaurant-menu-svc.herokuapp.com/item?category=" +
shortName;
fetch(url)
.then(data => data.json())
.then(element => {
this.setState({ items: element });
});
}
if (this.state.items !== "") {
let selectedMenu = this.state.items;
console.log(selectedMenu);
return selectedMenu.map(item => {
return <div key={item.name}> {item.name}</div>;
});
}
};
render() {
return <div>{this.renderItems()}</div>;
}
}
export default MenuItems;
Let's call App a parent and MenuCategory a child.
Let's denote a function call as the '->' sign.
There is an infinite loop formed like that:
child.render -> child.menuCat -> child.props.callback -> parent.handleProps -> parent.setState -> parent.render -> child.render.
My React app has three components. Two of them are child components and the other is parent. I need to pass a data (projectId) from one child component to the other child through the parent component and after receiving the data, fire a function. As my example, I'm sending projectId from ChildOne to Parent and then send projectId from Parent to ChildTwo. ChildTwo has a function called setProject(projectId) and I need to fire it once the projectID is received. The problem is I can't get the function getProjectId fired in ChildTwo by clicking on the button in ChildOne. I also tried with componentDidMount and componentWillReceiveProps which are not working for me. How can I do this?
Here what I tried
ChildOne :
class ChildOne extends React.Component {
constructor(props) {
super(props);
this.state = {
projectId: 3,
};
}
sendProjectId = (projectId) => {
this.props.sendId(projectId)
}
render() {
return(
<button onClick={() => this.sendProjectId(this.state.projectId)}>
Click
</button>
)
}
}
Parent:
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
projectId: '',
};
}
getId = (proId) => {
this.setState({
projectId : proId
})
}
render() {
return(
<div>
<CildOne sendId={this.getId} />
<CildTwo sendOneId={this.state.projectId} />
</div>
)
}
}
ChildTwo:
class ChildTwo extends React.Component {
constructor(props) {
super(props);
this.state = {
projectId: '',
};
}
getProjectId = (this.props.sendOneId) => {
//Do something with this.props.sendOneId
}
render() {
return(
<div></div>
)
}
}
This would depend on what ChildTwo wants to accomplish with the said data.
Case 1:
ChildTwo intends to fetch some data with the corresponding projectId and display it in the component. Then, you can easily fetch this data in the parent component and pass the data down as props.
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
projectId: '',
dataForChildTwo: null,
};
}
getId = (proId) => {
this.setState({
projectId : proId,
dataForChildTwo: fetchData(proId)
})
}
render() {
return(
<div>
<CildOne sendId={this.getId} />
<CildTwo data={this.state.dataForChildTwo} />
</div>
)
}
}
Case 2:
ChildTwo intends to make some change to something inside it when projectId changes. Then you can use componentDidUpdate hook to see if prop changed and respond to it.
class ChildTwo extends React.Component {
constructor(props) {
super(props);
this.state = {
projectId: '',
};
}
getProjectId = (this.props.sendOneId) => {
//Do something with this.props.sendOneId
}
componentDidUpdate(prevProps) {
if(this.props.projectId!==prevProps.projectId) {
// do something
}
}
render() {
return(
<div></div>
)
}
}
Case 3:
If none of the above cases work for you, then you can manually reload the complete component when the projectId changes using a key attribute:
<CildTwo key={this.state.projectId} sendOneId={this.state.projectId} />
Note: This reloads the whole component quite unnecessarily.
You did a mistake in getProjectId function of ChildTwo component.
Your function cannot receive anything as a parameter from prop.
So, your function should look like:
getProjectId = (sendOneId) => {
//Do something with this.props.sendOneId
}
Then you should use componentWillReceiveProps like this:
componentWillReceiveProps(nextProps) {
if (this.props.sendOneId !== nextProps.sendOneId) {
this.getProjectId(nextProps.sendOneId);
}
}
Here is a working codesandbox example that I created to fix your problem:
https://codesandbox.io/s/5v4rn7qnll
You should probably use componentDidUpdate with a condition to check to see whether the projectId in state needs to be updated when sendOneId changes. You can then use setStates callback to call getProjectId:
componentDidUpdate() {
const { projectId: currentProjectId } = this.state;
const { sendOneId: projectId } = this.props;
if (projectId !== currentProjectId) {
this.setState({ projectId }, () => this.getProjectId());
}
}
Full working example:
class ChildOne extends React.Component {
constructor(props) {
super(props);
this.state = {
projectId: 3,
};
}
sendProjectId = (projectId) => {
this.props.sendId(projectId)
}
render() {
return (
<button onClick={() => this.sendProjectId(this.state.projectId)}>
Click
</button>
);
}
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
projectId: '',
};
}
getId = (projectId) => {
this.setState({ projectId });
}
render() {
return (
<div>
<ChildOne sendId={this.getId} />
<ChildTwo sendOneId={this.state.projectId} />
</div>
)
}
}
class ChildTwo extends React.Component {
constructor(props) {
super(props);
this.state = {
projectId: '',
};
}
componentDidUpdate() {
const { projectId: currentProjectId } = this.state;
const { sendOneId: projectId } = this.props;
if (projectId !== currentProjectId) {
this.setState({ projectId }, () => this.getProjectId());
}
}
getProjectId = () => {
console.log(this.state.projectId);
}
render() {
return (
<div></div>
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById('container')
);
<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>
<div id="container"></div>
Our you can try a functional component or hooks if you want to set some state
function ChildOne(props) {
const [projectId, setProjectId] = useState(3);
function sendProjectId(data){
props.sendId(projectId)
}
return(
<button onClick={() => sendProjectId(projectId)}>
Click
</button>
)
}
function ChildTwo(props) {
const [state, setState] = useState('')
function getProjectId(data) {
//Do something with this.props.sendOneId
console.log(`data here ${data}`)
return false;
}
getProjectId(props.sendOneId)
return (
<div>
</div>
)
}
function Parent(){
const [projectId, setProjectId] = useState('');
function getId(proId) {
setProjectId(proId)
}
return(
<div>
<ChildOne sendId={getId} />
<ChildTwo sendOneId={projectId} />
</div>
)
}
I have a main react component called 'App' which contain user input data in its state. The state is updated with setState() every time user enter new data. Then the state is passed as props to another component called 'IncomeList' which render the data on screen. However the IncomeList component is not getting updated state after user input some data.
class App extends React.Component {
constructor(props) {
super(props);
this.addData = this.addData.bind(this);
this.state = {
expenses: [],
income: [],
}
}
addData(data) {
if (data.type == 'income') {
this.setState((prevState) => {
income: prevState.income.push(data);
}, console.log(this.state.income));
} else if (data.type == 'expense') {
this.setState((prevState) => {
expenses: prevState.expenses.push(data);
})
}
}
render() {
return (
<div>
<UserInput addData={this.addData} />
<IncomeList income={this.state.income} />
</div>
);
}
}
// UserInput component which contain a form
class UserInput extends React.Component {
constructor(props) {
super(props);
this.addDataLocal = this.addDataLocal.bind(this);
}
addDataLocal(e) {
e.preventDefault();
const data = {
type: e.target.elements.type.value,
description: e.target.elements.description.value,
amount: e.target.elements.amount.value
}
this.props.addData(data);
}
render() {
return (
<div>
<form onSubmit={this.addDataLocal}>
<select name="type" id="input-type" name="type">
<option value="income">Income</option>
<option value="expense">Expense</option>
</select>
<input type="text" placeholder="decription..." name="description"/>
<input type="number" placeholder="amount..." name="amount"/>
<input type="submit" value="Add"/>
</form>
</div>
)
}
}
class IncomeList extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{
this.props.income.map((item) => {
return (
<IncomeListItem key={item.amount} item={item}/>
)
})
}
</div>
)
}
}
You don't return anything from this.setState. You need return an object to be merged with your current state.
addData(data) {
const key = data.type === 'income' ? 'income' : 'expenses';
this.setState(prevState => ({
// with Computed Property Names we can make our
// code concise and avoid conditionals
[key]: [...prevState[key], data]
}), console.log(this.state[key]));
}
your addData should be like this
addData(data) {
if (data.type == 'income') {
let income=[...this.state.income];
income.push(data);
this.setState({
income:income
})
} else if (data.type == 'expense') {
let expenses=[...this.state.expenses];
expenses.push(data);
this.setState({
expenses:expenses
});
}
}
With #Asaf Aviv input I have created a working fiddle. Hope this will help.
JS Fiddle