So I am trying to build my first react.js app and am wondering, why my page does neither update after POSTING a new player to my app via axios, nor after DELETING one. As I refresh the page afterwards the players are created or gone depending on the operation. So the API part is working just fine.
I have a PlayerPage.js like so:
PlayerPage.js
import React, { Component } from 'react';
import axios from 'axios';
import PlayerForm from './playerform';
import PlayCard from './playercard';
class PlayerPage extends Component {
constructor(props) {
super(props);
}
state = {
players: []
};
componentDidMount() {
axios
.get('http://localhost:3001/player')
.then(res => {
const players = res.data;
this.setState({ players });
})
.catch(console.log);
}
render() {
return (
<div>
<h2>Add Player</h2>
<PlayerForm />
<hr></hr>
<h2>Available Player</h2>
{this.state.players.map(player => (
<PlayCard player={player} key={player.id} />
))}
</div>
);
}
}
export default PlayerPage;
It does reference (and fill in from API) the PlayerForm and PlayerCard component, which look like this:
playerform.js
import React, { Component } from 'react';
import axios from 'axios';
import {
FormControl,
FormGroup,
FormLabel,
Form,
Button
} from 'react-bootstrap';
class PlayerForm extends Component {
state = {
name: '',
nickname: ''
};
handleSubmit = e => {
e.preventDefault();
axios
.post('http://localhost:3001/player', JSON.stringify(this.state), {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
})
.then()
.catch(err => console.log(err));
};
handelChange = e => {
this.setState({
[e.target.name]: e.target.value
});
};
render() {
return (
<Form onSubmit={this.handleSubmit}>
<FormGroup>
<FormLabel>Name</FormLabel>
<FormControl
type="text"
name="name"
placeholder="Enter player name"
onChange={this.handelChange.bind(this)}
/>
</FormGroup>
<FormGroup>
<FormLabel>Nickname</FormLabel>
<FormControl
type="text"
name="nickname"
placeholder="Enter player nickname"
onChange={this.handelChange.bind(this)}
/>
</FormGroup>
<Button variant="btn btn-primary" type="submit">
Add
</Button>
</Form>
);
}
}
export default PlayerForm;
and playercard.js:
import React, { Component } from 'react';
import { Card, Button } from 'react-bootstrap';
import PropTypes from 'prop-types';
import axios from 'axios';
class PlayerCard extends Component {
constructor(props) {
super(props);
this.player = props.player;
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
axios
.delete(`http://localhost:3001/player/${this.player.id}`)
.then()
.catch(console.log);
}
render() {
return (
<Card className="flex-row flex-wrap">
<Card.Header>
<Card.Img variant="left" src="https://via.placeholder.com/150" />
</Card.Header>
<Card.Body variant="center">
<Card.Title>{this.player.name}</Card.Title>
<Card.Subtitle>{this.player.nickname}</Card.Subtitle>
</Card.Body>
<Card.Body variant="right">
<Button variant="btn btn-primary" onClick={this.handleClick}>
Delete
</Button>
</Card.Body>
</Card>
);
}
}
PlayerCard.propTypes = {
player: PropTypes.object.isRequired
};
export default PlayerCard;
So the question is, why does my side not update properly?
I am grateful for any hints. Thanks in advance.
Bonus question: How do I fetch the ip and port dynamically depending on where the node server is running?
Why it's not working:
Because you are not refreshing the list view when the form is submitted.
React is not magic, you need to tell when you want the view to be rerender.
One way to to this is adding a onCreate prop to your playerform.js and run the function when the form is successfully submitted.
import React, { Component } from 'react';
import axios from 'axios';
import PlayerForm from './playerform';
import PlayCard from './playercard';
class PlayerPage extends Component {
constructor(props) {
super(props);
}
state = {
players: []
};
loadPlayers() {
axios
.get('http://localhost:3001/player')
.then(res => {
const players = res.data;
this.setState({ players });
})
.catch(console.log);
}
componentDidMount() {
this.loadPlayers();
}
render() {
return (
<div>
<h2>Add Player</h2>
<PlayerForm onCreate={() => this.loadPlayers()}/>
<hr></hr>
<h2>Available Player</h2>
{this.state.players.map(player => (
<PlayCard player={player} key={player.id} />
))}
</div>
);
}
}
export default PlayerPage;
import React, { Component } from 'react';
import axios from 'axios';
import {
FormControl,
FormGroup,
FormLabel,
Form,
Button
} from 'react-bootstrap';
class PlayerForm extends Component {
state = {
name: '',
nickname: ''
};
handleSubmit = e => {
e.preventDefault();
axios
.post('http://localhost:3001/player', JSON.stringify(this.state), {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
})
.then(() => {
this.props.onCreate();
})
.catch(err => console.log(err));
};
handelChange = e => {
this.setState({
[e.target.name]: e.target.value
});
};
render() {
return (
<Form onSubmit={this.handleSubmit}>
<FormGroup>
<FormLabel>Name</FormLabel>
<FormControl
type="text"
name="name"
placeholder="Enter player name"
onChange={this.handelChange.bind(this)}
/>
</FormGroup>
<FormGroup>
<FormLabel>Nickname</FormLabel>
<FormControl
type="text"
name="nickname"
placeholder="Enter player nickname"
onChange={this.handelChange.bind(this)}
/>
</FormGroup>
<Button variant="btn btn-primary" type="submit">
Add
</Button>
</Form>
);
}
}
export default PlayerForm;
I didn't check if the prop is present but normally you should.
How to handle API route
You should use environments variables:
.env
API_HOST=http://localhost:3001/
process.env.API_HOST
But it's depending on your development environment.
As all suggested, you are not updating the state of the PlayerPage component. Even though answer suggested by #tristan works, I would suggest to reduce API calls as much as possible. My solution would be as following:
PlayerPage.js
import React, { Component } from 'react';
import axios from 'axios';
import PlayerForm from './playerform';
import PlayCard from './playercard';
class PlayerPage extends Component {
constructor(props) {
super(props);
}
state = {
players: []
};
componentDidMount() {
axios
.get('http://localhost:3001/player')
.then(res => {
const players = res.data;
this.setState({ players });
})
.catch(console.log);
}
handleAddNewPlayer = player => {
this.setState(prevState => ({
players: [...prevState.players, player] // or [player, ...prevState.players]
}))
}
handleRemovePlayer = id => {
const players = this.state.filter(player => player.id !== id)
this.setState({ players })
}
render() {
return (
<div>
<h2>Add Player</h2>
<PlayerForm handleAddNewPlayer={this.handleAddNewPlayer}/>
<hr></hr>
<h2>Available Player</h2>
{this.state.players.map(player => (
<PlayCard player={player} key={player.id} handleRemovePlayer={this.handleRemovePlayer} />
))}
</div>
);
}
}
export default PlayerPage;
playerform.js
import React, { Component } from 'react';
import axios from 'axios';
import {
FormControl,
FormGroup,
FormLabel,
Form,
Button
} from 'react-bootstrap';
class PlayerForm extends Component {
state = {
name: '',
nickname: ''
};
handleSubmit = e => {
e.preventDefault();
axios
.post('http://localhost:3001/player', JSON.stringify(this.state), {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
})
.then(data => this.props.handleAddNewPlayer(data)) // adjust this api call to return added user with id
.catch(err => console.log(err));
};
handelChange = e => {
this.setState({
[e.target.name]: e.target.value
});
};
render() {
return (
<Form onSubmit={this.handleSubmit}>
<FormGroup>
<FormLabel>Name</FormLabel>
<FormControl
type="text"
name="name"
placeholder="Enter player name"
onChange={this.handelChange.bind(this)}
/>
</FormGroup>
<FormGroup>
<FormLabel>Nickname</FormLabel>
<FormControl
type="text"
name="nickname"
placeholder="Enter player nickname"
onChange={this.handelChange.bind(this)}
/>
</FormGroup>
<Button variant="btn btn-primary" type="submit">
Add
</Button>
</Form>
);
}
}
export default PlayerForm;
and playercard.js:
import React, { Component } from 'react';
import { Card, Button } from 'react-bootstrap';
import PropTypes from 'prop-types';
import axios from 'axios';
class PlayerCard extends Component {
constructor(props) {
super(props);
this.player = props.player;
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
axios
.delete(`http://localhost:3001/player/${this.player.id}`)
.then(this.props.handleRemovePlayer(this.player.id))
.catch(console.log);
}
render() {
return (
<Card className="flex-row flex-wrap">
<Card.Header>
<Card.Img variant="left" src="https://via.placeholder.com/150" />
</Card.Header>
<Card.Body variant="center">
<Card.Title>{this.player.name}</Card.Title>
<Card.Subtitle>{this.player.nickname}</Card.Subtitle>
</Card.Body>
<Card.Body variant="right">
<Button variant="btn btn-primary" onClick={this.handleClick}>
Delete
</Button>
</Card.Body>
</Card>
);
}
}
PlayerCard.propTypes = {
player: PropTypes.object.isRequired
};
export default PlayerCard;
The lifecycle method componentDidMount() doesn't run in PlayerPage.js every time the state changes, just once when it first loads onto the page. This means the GET request is only run once when the page loads.
You need to either call the GET route again after a POST or DELETE happens, or update the app's state with the data from the POST/DELETE call when it happens to put that data onto the page as well as into the database.
To update the PlayerCard list, the players state must be updated in PlayerPage.
Since the parent has no way to get new players after the Rest API call, it seems that we need a callback function to update the parent component as a child.
I assume you are not using any state management tool(Redux, Flux), and only react is in place and also you don't want to call the api again.
Inside PlayerPage component make a function
addPlayer=(player)=>{
this.setState({
players: {...players, player}
})
}
}
and in render of the same component
Inside PlayerCard
handleClick() {
axios
.delete(`http://localhost:3001/player/${this.player.id}`)
.then((response)=>{
you get your output pass it to props
var player=response.data
this.props.addPlayer(response)
})
.catch(console.log);
}
This example is only with reference to the addPlayer . You can customise it according to your requirement
Related
I want to get the value of TextField input and render the message conditionally. I tried this one, its working but this one is functioning dynamically because I used onChange. I want to achieve the same but using onSubmit on <Button> Is there anyway to do that?
import React from 'react';
import { Component } from 'react';
import Button from '#mui/material/Button';
import { TextField } from '#mui/material';
class App extends Component
{
state = {
myValue: null,
}
handleChange = (e) => this.setState({
myValue: e.target.value
})
render() {
return (
<div>
<TextField
value={this.state.myValue}
onSubmit={this.handleChange}
/>
<button >Get Weather</button>
{this.state.myValue ? <p>value inputed </p>: <p>no input</p>}
</div>
)
}
}
export default App;
Using Refs is what you need. You can get the current value of your input by clicking a button and only then change the state.
Demo
import React, { createRef } from "react";
import { Component } from "react";
import { TextField } from "#mui/material";
class App extends Component {
constructor(props) {
super(props);
this.textInput = createRef();
this.state = {
myValue: ""
};
}
showRefContent = () => {
this.setState({
myValue: this.textInput.current.value
});
};
handleChange = (e) =>
this.setState({
myValue: e.target.value
});
render() {
return (
<div>
<TextField inputRef={this.textInput} />
<button onClick={this.showRefContent}>Get Weather</button>
<p>
{this.state.myValue.length > 0
? `text:${this.state.myValue}`
: "no text"}
</p>
</div>
);
}
}
export default App;
you just need to you onkeydown instead onsubmit.
<TextField
value={this.state.myValue}
onKeyDown={this.handleChange}
/>
or use
<form onSubmit={handleChange }>
<TextField
value={this.state.myValue}
onKeyDown={this.handleChange}
/>
<button type="submit">submit</button>
</form>
SearchBox.js File is:
import React from 'react';
const SearchBox = ({ searchfield, searchChanger}) => {
return (
<div className='pa2'>
<input
className='pa3 b--green bg-light-blue'
type='search'
placeholder='search robots'
onChange={searchChange}
/>
</div>
);
}
export default SearchBox;
App.js File is:
import React, { Component } from 'react';
import CardList from './CardList';
import SearchBox from './SearchBox';
import { robots } from './robots';
import './App.css';
class App extends Component {
constructor() {
super()
this.state = {
robots: robots,
searchfield:''
}
}
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json())
.then(users => {this.setState({ robots: robots})});
}
onSearchChange = (event) => {
this.setState({ searchfield: event.target.value })
}
render() {
const filteredRobots = this.state.robots.filter(robots => {
return robots.name.toLowerCase().includes(this.state.searchfield.toLowerCase());
})
if (robots.length ===0) {
return <h1>Loading</h1>
}
else {
return (
<div className='tc'>
<h1 className='f1'>RoboFriends</h1>
<SearchBox searchChange={this.onSearchChange}/>
<CardList robots={filteredRobots} />
</div>
);
}
}
}
export default App;
I was able to get rid of this error just by changing the order of import in App.js file, but later on its showing this error no matter what i do?
please help in case I have any typing issue, if no then what is the problem
Your SearchBox should be:
const SearchBox = ({ searchfield, searchChange}) => {
return (
<div className='pa2'>
<input
className='pa3 b--green bg-light-blue'
type='search'
placeholder='search robots'
onChange={searchChange}
/>
</div>
);
}
export default SearchBox;
In your props there was searchChanger which is incorrect.
I'm new to React, and despite many attempts I can't figure out how to set my id dynamically in axios requests in my edit-form.
I tried to set the axios requests with a static number just to test my edit-form, I can log my object but when I submit it deletes the product data instead of updating with the new data because the id is undefined.
I think I just need to find out what to write in my componentDidMount and handleSubmit to catch the id.
For example ${this.state.product.id} is undefined, why isn't it working?
It's my first question, so I hope I am clear and tell me if you need more code.
EditForm
import React, { Component } from 'react';
import { withRouter } from 'react-router';
import axios from 'axios';
import Navbar from 'react-bootstrap/Navbar';
import Nav from 'react-bootstrap/Nav';
import Form from 'react-bootstrap/Form';
import Col from 'react-bootstrap/Col';
import FormControl from 'react-bootstrap/FormControl';
import Button from 'react-bootstrap/Button';
class EditForm extends React.Component {
constructor(props) {
super(props);
this.state = { product: [] };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
};
componentDidMount = () => {
axios
.get(`/products/edit-form/${this.props.match.params.id}`)
.then(response => {
console.log(response.data.products);
this.setState({
product: response.data.products
})
});
};
handleChange(e) {
this.setState({id: e.target.value})
this.setState({reference: e.target.value})
this.setState({designation: e.target.value})
}
handleSubmit(e) {
e.preventDefault();
const data = {
id: this.state.product.id,
reference: this.state.product.reference,
designation: this.state.product.designation
};
axios
.post(`/products/${this.props.match.params.id}`, data )
.then(res => console.log(res))
.catch(err => console.log(err));
};
renderForm() {
return this.state.product.map((product, index) => {
const { id,reference,designation } = product
return(
<>
<Form className="post" onSubmit={this.handleSubmit}>
<Form.Row>
<Form.Group as={Col} controlId="formGridReference">
<Form.Label>Reference</Form.Label>
<Form.Control type="text" value={this.state.product.reference}
onChange={this.handleChange} name="reference" placeholder={reference} />
</Form.Group>
<Form.Group as={Col} controlId="formGridDesignation">
<Form.Label>Designation</Form.Label>
<Form.Control type="text" value={this.state.product.designation}
onChange={this.handleChange} name="designation" placeholder={designation} />
</Form.Group>
</Form.Row>
<Button variant="primary" type="submit">
Submit
</Button>
</Form>
</>
);
})
}
render() {
return (
<div>
<h1>Formulaire de modification</h1>
{this.renderForm()}
</div>
);
}
}
export default withRouter(EditForm);
App.js
class App extends Component {
render() {
return (
<React.Fragment>
<NavBar />
<Router>
<Route exact path="/" component={Products}/>
<Route path="/edit-form/:productId" component={EditForm}/>
</Router>
</React.Fragment>
);
}
}
Products
import React, { Component } from 'react';
import Table from 'react-bootstrap/Table';
import Button from 'react-bootstrap/Button';
import { Link } from 'react-router-dom';
import axios from 'axios';
const headings = [
'id','reference','designation'
];
export default class Products extends Component {
constructor(props) {
super(props);
this.state = {
products: []
};
};
componentDidMount = () => {
axios.get("/products").then(response => {
console.log(response.data.products);
this.setState({
products: response.data.products
})
});
};
renderTableHeader() {
return headings.map((key, index) => {
return <th key={index}>{key.toUpperCase()}</th>
})
}
renderProductData() {
return this.state.products.map((product, index) => {
const { id,reference,designation } = product
return (
<tr key={id}>
<td>
{id}
<Link to={`/edit-form/${id}`}>Modifier</Link>
</td>
<td>{reference}</td>
<td>{designation}</td>
</tr>
)
})
}
render() {
return (
<div>
<h1 id='title'>Produits</h1>
<Table striped bordered hover id='products'>
<thead>
{this.renderTableHeader()}
</thead>
<tbody>
{this.renderProductData()}
</tbody>
</Table>
</div>
);
}
}
Async functions
// productController.js
const getProduct = async (req, res) =>
{
const { productId } = req.params;
const product = await productDb.getProduct(productId);
res.status(200).send({ products: product.rows });
};
const postProduct = async (req, res) =>
{
const { productId } = req.params;
const { reference,designation } = req.body;
await productDb.updateProduct(productId, reference, designation);
res.status(200).send(`Le produit reference ${reference} a été modifié`);
console.log(`Le produit reference ${reference} a été modifié`);
// productDb.js
const getProduct = async (id) =>
{
const connection = new DbConnection();
return await connection.performQuery(`SELECT * FROM produits WHERE id=${id}`);
};
const updateProduct = async (id, reference, designation) =>
{
const connection = new DbConnection();
await connection.performQuery("UPDATE produits SET reference=?, designation=? WHERE id=?",
[reference, designation, id]);
};
Thank you
In your Products.js, you are using links to open up your EditForm component. So, for you to be able to access the product id, you need to wrap your EditForm component with the withRouter hoc from the react-router library. This hoc will then make the match prop available to your EditForm component.
So, you need to add react-router to your dependencies by running this:
yarn add react-router **OR** npm install react-router
Then add it to your import statements in EditForm.js
import { withRouter } from 'react-router';
And then inside componentDidMount, you should be able to access the id by doing this:
this.props.match.params.id
You also need to change your class definition to:
class EditForm extends React.Component
And then export it like this:
export default withRouter(EditForm)
I assume in your routes file, you must have a route that is defined like this:
<Route path="/edit-form/:id" component={EditForm} />
I have a form that submit some data and i have item component that has a delete button that delete an item but when i submit something it submit and delete the item in the same time
import React, {Component} from 'react';
import ApiClient from './apiClient';
import './MessageForm.css';
class MessageForm extends Component {
constructor(props){
super(props);
this.state = {
submitted: false
}
}
handleSubmit = async (event) => {
event.preventDefault();
const messageData = new FormData(event.target);
await ApiClient.addMessage({
license_plate: messageData.get('license'),
body: messageData.get('body')
});
// console.log("submitted");
// this.props.refreshList();
};
render() {
return(
<form onSubmit={this.handleSubmit} className="MessageForm">
<div>
<label htmlFor="license">License Plate</label>
<input id="license" name="license" type="text" />
</div>
<div>
<label htmlFor="body">Message</label>
<textarea id="body" name="body" type="text"/>
</div>
<div>
<input type="submit" value="Submit"/>
</div>
</form>
)
}
};
export default MessageForm;
this is the item component
import React from 'react';
import moment from 'moment';
import SocailShare from './SocialShare.css'
import { FacebookShareButton, LinkedinShareButton,
TwitterShareButton,
TelegramShareButton,
WhatsappShareButton,
EmailShareButton,} from 'react-share';
import { FacebookIcon, EmailIcon,
TwitterIcon,
TelegramIcon,
WhatsappIcon,
LinkedinIcon,} from 'react-share';
import {
FacebookShareCount,
PinterestShareCount,
VKShareCount,
OKShareCount,
RedditShareCount,
TumblrShareCount,
} from 'react-share';
import './MessageItem.css';
export default ({ id, submission_date, license_plate, body, handleDelete }) => {
var timePosted = moment(submission_date).format('DD/MM/YYYY - HH:mm');
const onDelete = (id) => {
handleDelete(id);
}
return (
<li className="MessageItem">
<span>Time: {timePosted} - </span>
<span>To license: {license_plate} : </span>
<span> {body} </span>
<button onClick={onDelete(id)}>X</button>
<div className="SocialShare">
<FacebookShareButton url="https://github.com/nygardk/react-share#readme" >
<FacebookIcon size={30}/>
<FacebookShareCount url="https://github.com/nygardk/react-share#readme">
{shareCount => (
<span className="myShareCountWrapper">{shareCount}</span>
)}
</FacebookShareCount>
</FacebookShareButton>
<TwitterShareButton url="https://github.com/nygardk/react-share#readme">
<TwitterIcon size={30}/>
</TwitterShareButton >
<EmailShareButton url="https://github.com/nygardk/react-share#readme">
<EmailIcon size={30}/>
</EmailShareButton>
</div>
</li>
);
};
and this is the message list component that renders the message item
import React, { Component } from 'react';
import './MessageList.css';
import MessageItem from './MessageItem';
import ApiClient from './apiClient'
class MessageList extends Component {
constructor(props) {
super(props);
}
handleOnDelete = async (id) => {
console.log(id + "deleted")
await ApiClient.deleteMessage(id);
this.props.refreshList();
}
render() {
const {
messages
} = this.props;
messages.sort(function(a,b){
//the list will be ordered in descending date order (most recent first)
return new Date(b.submission_date) - new Date(a.submission_date);
});
const $messages = messages.map((message) => <MessageItem handleDelete={this.handleOnDelete} key={message._id} {...message} />);
return (
<section className="MessageList">
<h1>Message Board</h1>
<ul>
{$messages}
</ul>
</section>
)
}
}
export default MessageList;
and this is the app component where everything is rendered
import React, { Component} from 'react';
import {BrowserRouter, Route, Switch} from 'react-router-dom';
import ApiClient from './apiClient';
import './App.css';
import MessageForm from './MessageForm';
import MessageList from './MessageList';
class App extends Component {
constructor(props){
super(props);
this.state = {
messages: []
}
}
componentDidMount = async () => {
this.refreshList();
}
refreshList = async () => {
const messages = await ApiClient.getMessages();
this.setState({
messages
})
}
render () {
return (
<BrowserRouter>
<div className="App">
<header className="App-header">
<h1>Hello License</h1>
<p>Send your messages to a plate number easily!</p>
</header>
<MessageForm refreshList = {this.refreshList}/>
</div>
<Switch>
<Route exact path="/api" render ={props => <MessageList refreshList = {this.refreshList} messages={this.state.messages} {...props}/> }/>
</Switch>
</BrowserRouter>
)
}
};
export default App;
in your item component, this line <button onClick={onDelete(id)}>X</button> is your problem.
What you are inadvertently saying is that when the DOM renders this component, it should call onDelete right away, and the onClick handler will refer to void. To avoid this, what you want is to pass in a function like so: <button onClick={(id) => onDelete(id)}>X</button>
I am building a Meteor app with ReactJS and Semantic-UI for react. I have run into a problem when trying to create a form for a new item that appears as a modal. I receive the following error.
Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in. Check the render method of `App`.
App.jsx file:
import React, { Component, PropTypes } from 'react';
import { createContainer } from 'meteor/react-meteor-data';
import { Items } from '../api/items.js';
import { Item } from './Item.jsx';
import { ItemFormModal } from './ItemFormModal.jsx';
// App component - represents the whole app
export class App extends Component {
renderItems() {
return this.props.items.map((item) => (
<Item key={item._id} item={item} />
));
}
render() {
return (
<div className="container">
<header>
<h1>Products</h1>
<ItemFormModal />
</header>
<ul className="collection">
{this.renderItems()}
</ul>
</div>
);
}
}
App.propTypes = {
items: PropTypes.array.isRequired,
};
// creates container on client side for the collection
export default createContainer(() => {
return {
// fetch all the items available
items: Items.find({}, { sort: { createdAt: -1 } }).fetch(),
};
}, App);
EDIT: I have changed it to reflect the whole ItemFormModal.jsx:
import { Items } from '../api/items.js';
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
// Import all semantic resources
import { Button, Icon, Header, Form, Modal } from 'semantic-ui-react';
export default class ItemFormModal extends React.Component {
constructor(props) {
super(props);
this.state = {
title: "",
price: 0.00,
modalOpen: false
}
this.handleOpen = this.handleOpen.bind(this);
this.handleClose = this.handleClose.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleOpen(event) { this.setState({ modalOpen: true }) }
handleClose(event) { this.setState({ modalOpen: false }) }
handleInputChange(event) {
const target = event.target;
const value = target.type === "checkbox" ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
handleSubmit(event) {
event.preventDefault();
let title = this.state.title.trim();
let price = this.state.price;
Items.insert({
title: title,
price: price,
recurring: false,
createdAt: new Date(), // current time
});
this.setState({
title: "",
price: 0.00
});
}
render() {
return (
<div id="new-item">
<Button onClick={this.handleOpen}>Create</Button>
<Modal
open={this.state.modalOpen}
onClose={this.handleClose}
size="small"
closeIcon="close">
<Modal.Header>Create new item</Modal.Header>
<Modal.Content>
<Form>
<Form.Group>
<Form.Input name="title" label="Title" placeholder="Title" value={this.state.title} onChange={this.handleInputChange}/>
<Form.Input name="price" label="Price" placeholder="Price" value={this.state.price} onChange={this.handleInputChange} />
</Form.Group>
</Form>
</Modal.Content>
<Modal.Actions>
<Button className="negative" onClick={this.handleClose}>Cancel</Button>
<Button className="positive" onClick={this.handleSubmit}>Create</Button>
</Modal.Actions>
</Modal>
</div>
)
}
}
Any help is greatly appreciated!
you are importing into App.jsx incorrectly. you have this:
import { ItemFormModal } from './ItemFormModal.jsx';
... which will not work if your export is marked as default. you can either remove "default" from your export, or you can change your import to this:
import ItemFormModal from './ItemFormModal.jsx';