Hello I am new to ReactJS so I am just practising on working with states and also so a good practice for a starting point I thought why not the classic TODO App.
So I do not know why the object is not being rendered or being added because when I even console logged the object It did not even show that it's empty or anything the was literally no output so I do not know where I could have went wrong with this methods
Code Below App.js: This is the file that has all the methods and state control of the TODO APP
import React, { Component } from 'react';
import style from './stylesheet/app.css'
import ListItems from './ListItems'
class App extends Component{
constructor(props){
super(props);
this.state = {
items:[],
currentItem:{
notes: '',
key: ''
}
}
this.handleInput = this.handleInput.bind(this);
this.addItem = this.addItem.bind(this);
}
// Handling user Input to save on before I add to the Items
// this.state.currentItems is a temporary store place for TODO'S
handleInput(e){
this.setState({
currentItem: {
notes: e.target.value,
key: Date.now()
}
})
}
// After handling input input once the add button is clicked I want to add
// the the object in the temporary storage into the permanent store place that is the
// this.state.items --> permanent store place
addItem(e){
e.preventDefault()
const newTodo = this.state.currentItem;
if (newTodo.text !== " "){
const newTodos = [...this.state.items, newTodo];
this.setState({
items:newTodos,
currentItem:{
notes:'',
key:''
}
})
}
}
render(){
return(
<div className="container" style={style}>
<div className='todo-form'>
<form id="form">
<input type="text"
placeholder="Enter in your todo's"
value={this.state.currentItem.notes}
onChange={this.handleInput}></input>
<button type="submit" onSubmit={this.addItem}>Add Todo</button>
</form>
<ListItems items={this.state.items}/>
</div>
</div>
)
}
}
export default App
Code Below ListItems.js: This file contains code where I tried to map through the ojects to display the TODO'S
import React from 'react';
const ListItems = (props) =>{
const items = props.items;
const listItems = items.map(item =>{
return <div className="todo-list" key={item.key}> <p>{item.key}</p> </div>
})
return(
<div>
{listItems}
</div>
)
}
export default ListItems
Can you please help me figure out where I could be going wrong?
Your code works perfectly fine. Just add addItem function to form element, so it preventsDefault correctly and doesnt reload whole page:
<form id="form" onSubmit={this.addItem}>
See here: https://codesandbox.io/s/musing-gareth-vlkmx
Related
I'm using google places autocomplete api in my react code.When i kept this code in seperate file and was calling this component in another it was working properly.But when i combined place search input feild with others field in form its not working.Whats the issue when i combined with other field in form?
This code
import React from "react";
/* global google */
class SearchBar extends React.Component {
constructor(props) {
super(props);
this.autocompleteInput = React.createRef();
this.autocomplete = null;
this.handlePlaceChanged = this.handlePlaceChanged.bind(this);}
componentDidMount() {
this.autocomplete = new google.maps.places.Autocomplete(this.autocompleteInput.current,
{"types": ["geocode"]});
this.autocomplete.addListener('place_changed', this.handlePlaceChanged);
}
handlePlaceChanged(){
const place = this.autocomplete.getPlace();
this.props.onPlaceLoaded(place);
}
render() {
return (
<input ref={this.autocompleteInput} id="autocomplete" placeholder="Enter your address"
type="text"></input>
);
}
}
output of this:
in seperate file
code after integrating other files(form input)
import React from "react";
import Geocode from "react-geocode";
import DatePicker from 'react-datepicker';
import Scrollbars from 'react-custom-scrollbars';
require('react-datepicker/dist/react-datepicker.css');
// set Google Maps Geocoding API for purposes of quota management. Its optional but
recommended.
Geocode.setApiKey("API_KEY");
/* global google */
export default class Checkout extends React.Component {
constructor(props) {
super(props);
this.state = {
locality: "",
lat : 0,
lng: 0,
otherState...
}
this.autocompleteInput = React.createRef();
this.autocomplete = null;
this.handlePlaceChanged = this.handlePlaceChanged.bind(this);
}
componentDidMount() {
this.autocomplete = new google.maps.places.Autocomplete(this.autocompleteInput.current,
{"types": ["geocode"]});
this.autocomplete.addListener('place_changed', this.handlePlaceChanged);
}
handlePlaceChanged(){
const place = this.autocomplete.getPlace().formatted_address;
//this.props.onPlaceLoaded(place);
this.setState({locality: place})
Geocode.fromAddress(this.state.locality).then(
response => {
const { lat, lng } = response.results[0].geometry.location;
console.log(lat, lng);
this.setState({
lat: lat,
lng: lng
})
},
error => {
console.error(error);
}
);
}
render() {
let publicUrl = process.env.PUBLIC_URL+'/'
let items = JSON.parse(localStorage.getItem("items"));
return (
// checkout page
<div className="contact-area pd-top-20 pd-bottom-65">
<div className="container">
<form className="contact-form-wrap contact-form-bg" onSubmit={e =>
this.handleSubmit(e)}>
<h4>Checkout</h4>
...other input feilds
<div className="row">
<div className="col-10 col-md-11" >
<h4>Select/Add new address</h4>
<div className="rld-single-input">
<label>Enter new address</label>
<input className="mb-2" ref={this.autocompleteInput} id="autocomplete"
placeholder="Enter Locality"
type="text"></input>
<input placeholder="Enter flat no./Bilding name" onChange={(e) =>
this.handleLandmark(e)}/>
</div>
</div>
</div>
</div>
</form>
Output after adding all code into one file
Accessing this.state immediately after calling this.setState({...}) is not a guaranteed operation because it's asynchronous read this react FAQ.
So what I will advice you do is pass a callback as second argument to this.setState(newState, callback), and your callback should contain the whole body of Geocode.fromAddress(...) while you access your state from inside your callback.
Codesandbox: https://codesandbox.io/s/github/adamschwarcz/react-firebase-app
I am really new to react and firebase and I followed this tutorial to come up with this app (full project – github link here) – it's an "Add your Wish app"
My problem is that I cannot store clap count on each post to my firebase – this component is called LikeButton.js.
I have been trying to add some similar firebase code (handleChange, handleSubmit, componentDidMount... etc.. etc..) as I learned in the tutorial to LikeButton.js to store the total amount of counts in firebase each time the button is clicked and the amount of claps incremented by +1.
Simply what I want – everytime the clap button is clicked and the initial ('0') state of count is incremented to +1 the current count is going to be updated into the database.
Just cannot come up with solution, can somebody please help?
My LikeButton.js code without any firebase:
import React, { Component } from 'react'
import firebase from '../../firebase.js';
import { makeStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import './Like.css';
class LikeButton extends Component {
state = {
count: 0,
}
incrementLike = () => {
let newCount = this.state.count + 1
this.setState({
count: newCount
})
console.log(this.state.count);
}
render() {
return(
<div class="counter">
<Button type="submit" color="primary" onChange={this.handleCount} onClick={this.incrementLike}>{this.state.count} 👏</Button>
</div>
)
}
}
export default LikeButton
My Add.js code with firebase:
import React, { Component } from 'react';
import firebase from '../../firebase.js';
import Button from '#material-ui/core/Button';
import TextField from '#material-ui/core/TextField';
import FadeIn from "react-fade-in";
import Placeholder from '../Placeholder/Placeholder.js';
import LikeButton from '../Like/Like.js'
import './Add.css';
class Add extends Component {
constructor() {
super();
this.state = {
loading: true,
currentItem: '',
username: '',
items: []
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({
[e.target.name]: e.target.value
});
}
handleSubmit(e) {
e.preventDefault();
const itemsRef = firebase.database().ref('items');
const item = {
title: this.state.currentItem,
user: this.state.username
}
itemsRef.push(item);
this.setState({
currentItem: '',
username: ''
});
}
componentDidMount() {
fetch("https://jsonplaceholder.typicode.com/posts")
.then(response => response.json())
.then(json => {
setTimeout(() => this.setState({ loading: false }), 1500);
});
const itemsRef = firebase.database().ref('items');
itemsRef.on('value', (snapshot) => {
let items = snapshot.val();
let newState = [];
for (let item in items) {
newState.push({
id: item,
title: items[item].title,
user: items[item].user
});
}
this.setState({
items: newState
});
});
}
removeItem(itemId) {
const itemRef = firebase.database().ref(`/items/${itemId}`);
itemRef.remove();
}
render() {
return (
<div className="container">
<div className="wrap">
<section className="add-item">
<h1>Napíš svoj wish</h1>
<h3>Možno prilepíš sebe, možno posunieš firmu.</h3>
<form onSubmit={this.handleSubmit}>
<TextField
id="filled-required"
label="Meno"
name="username"
variant="filled"
value={this.state.username}
onChange={this.handleChange}
/>
<TextField
required
id="standard-multiline-flexible"
label="Tvoje prianie"
name="currentItem"
variant="filled"
multiline
rows="6"
rowsMax="8"
value={this.state.currentItem}
onChange={this.handleChange}
/>
<Button
type="submit"
variant="contained"
color="primary">
Poslať wish
</Button>
</form>
</section>
<section className='items-list'>
<div className="item">
<div>
{this.state.items.map((item) => {
return (
<div>
{this.state.loading ? (
<>
<FadeIn>
<Placeholder />
</FadeIn>
</>
) : (
<div className="wish" key={item.id}>
<FadeIn>
<h2>{item.title}</h2>
<div className="name">
<p>poslal <span>{item.user}</span></p>
<LikeButton />
</div>
</FadeIn>
</div>
)}
</div>
)
})}
</div>
</div>
</section>
</div>
</div>
);
}
}
export default Add
First of all, you need to tell the LikeComponent which Wish it will be updating, and you will also need to be able to access the clapCount of the wish from the LikeComponent. This can be done easily using props. You should re-configure LikeComponent to accept a prop similar to wish, which would be the wish that you are displaying and modifying.
So, this line in Add.js
<LikeButton />
would instead look like <LikeButton wish={item} />. This way, your LikeComponent can access the item/wish.
Next, in the LikeComponent, you need to remove the local state and instead use the clap count stored in Firebase. Luckily, since you're passing the wish via a prop, you can simply refactor the LikeComponent to look like this:
class LikeButton extends Component {
incrementLike = () => {
// TODO: Implement clap incrementation via Firebase updates
}
render() {
return(
<div class="counter">
<Button type="submit" color="primary" onClick={this.incrementLike}>{this.props.wish.clapCount} 👏</Button>
</div>
)
}
}
Next, we need to actually implement incrementLike. Luckily, since we are getting the wish item passed to us via the wish prop, we can easily update it like so:
incrementLike = () => {
// get a reference to the item we will be overwriting
const wishRef = firebase.database().ref(`/items/${this.props.wish.id}`);
// get the current value of the item in the database
wishRef.once('value')
.then(snapshot => {
// get the value of the item. NOTE: this is unsafe if the item
// does not exist
let updatedWish = snapshot.val();
// update the item's desired property to the desired value
updatedWish.clapCount = updatedWish.clapCount + 1;
// replace the item with `wish.id` with the `updatedWish`
wishRef.set(updatedWish);
});
}
While this should work with only a few tweaks, I'm sure there's a better way to do it. You might even be able to avoid the call to once('value') since you're passing wish as a prop to LikeComponent. You should play around with it.
However, I strongly encourage you to explore migrating to Firebase Cloud Firestore. It's API is way more straightforward (in my opinion) than Realtime Database.
I'm creating a project-planning app using React, Redux, and Firebase. A single project record in my Firestore database contains a Title and some Content. When I go to update a project, I have the input fields' defaultValues set to the correct data for the project I want to edit. However, updating only works if I make changes to both the Content and Title input fields. Otherwise, upon submitting these values the data gets deleted because the local state has not seen any changes and therefore updates the untouched field to the empty string: ""
I have tried setting the local state of the EditProject component in the render method, but this is not possible:
render() {
const { project, auth } = this.props;
if (!auth.uid) return <Redirect to="/signin" />;
if (project) {
this.setState({
title: project.title,
content: project.content
});
...
I have also tried setting the state in during componentDidMount like so:
componentDidMount = () =>{
const { project } = this.props;
this.setState({
title: project.title,
content: project.content
})
}
But the issue with this is that the project prop does not get mapped by mapStateToProps before componentDidMount
Lastly, I've tried passing the project prop from the parent component, which is projectDetails, but I am unable to successfully do so. I might be doing this part wrong so please let me know if there is a good way to do this with the code I have. In ProjectDetails:
<Link to={"/edit/" + docId} key={docId}>
<button className="btn pink lighten-1 z-depth-0">Edit</button>
</Link>
This links to the 'broken' EditDetails component I am trying to fix.
Here is my code for the EditProject component
class EditProject extends Component {
state = {
title: "",
content: ""
};
handleChange = e => {
this.setState({
[e.target.id]: e.target.value
});
};
handleSubmit = e => {
e.preventDefault();
let localProject = this.state;
let docId = this.props.docId;
this.props.editProject(localProject, docId);
const projectDetailURL = "/project/" + docId;
this.props.history.push(projectDetailURL);
};
render() {
const { project, auth } = this.props;
if (!auth.uid) return <Redirect to="/signin" />;
if (project) {
return (
<div className="container section project-details">
<div className="card z-depth-0">
<div className="card-content">
<form onSubmit={this.handleSubmit} className="white">
<h5 className="grey-text text-darken-3">Edit Project</h5>
<div className="input-field">
<label htmlFor="title" className="active">
Title
</label>
<input
onChange={this.handleChange}
type="text"
id="title"
defaultValue={project.title}
/>
</div>
<div className="input-field">
<label htmlFor="content" className="active">
Edit Project Content
</label>
<textarea
id="content"
onChange={this.handleChange}
className="materialize-textarea"
defaultValue={project.content}
/>
</div>
<div className="input-field">
<button className="btn pink lighten-1 z-depth-0">
Update
</button>
</div>
</form>
</div>
<div className="card-action grey lighten-4 grey-text">
<div>
Posted by {project.authorFirstName} {project.authorLastName}
</div>
<div>{moment(project.createdAt.toDate()).calendar()}</div>
<div className="right-align" />
</div>
</div>
</div>
);
} else {
return (
<div className="container center">
<p>Loading project...</p>
</div>
);
}
}
}
const mapStateToProps = (state, ownProps) => {
//id = the document id of the project
const id = ownProps.match.params.id;
const projects = state.firestore.data.projects;
const project = projects ? projects[id] : null;
return {
project: project,
auth: state.firebase.auth,
docId: id
};
};
const mapDispatchToProps = dispatch => {
return {
editProject: (project, docId) => dispatch(editProject(project, docId))
};
};
export default compose(
connect(
mapStateToProps,
mapDispatchToProps
),
firestoreConnect([
{
collection: "projects"
}
])
)(EditProject);
Upon visiting the edit page, I would like the data to remain unchanged if a user does not make any changes to an input field.
I was able to properly update my local state by using React Router to pass props to my EditProject component from its "parent component". I used the React router to do this since the EditProject component is not actually nested inside this "parent component".
Here's how you can pass props to other components using React Router:
Specify where you want to send your props and what you want to send:
//ProjectDetails Component
<Link to={{
pathname: "/edit/" + docId,
state: {
title: project.title,
content: project.content
}
}}>
<button className="btn">Edit</button>
</Link>
Aquire props in the componentDidMount() lifecycle method and update the local state using setState().
//EditProject Component (component recieving props from ProjectDetails)
class EditProject extends Component {
state = {
title: "",
content: ""
};
componentDidMount = () => {
//Aquire proprs from React Router
const title = this.props.location.state.title
const content = this.props.location.state.content
//Update the local state
this.setState({
title: title,
content: content
})
}
I hope this helps!
What I want to do is to be able to toggle an active class on my elements that are dynamically created, as to be able to change the css for the selected checkbox, giving the impression that a certain filter is selected. I have looked at so many solutions and guides to make this work for my app, but I can't seem to implement it correctly. Any help would be appreciated.
Checkboxes component
import React from 'react';
const Checkbox = (props) => {
const { label, subKey } = props;
const sub1 = `${subKey}1`;
return (
<label htmlFor={sub1} className="check_label">
{label}
<input
type="checkbox"
id={sub1}
checked={props.isChecked}
onChange={props.handleCheck}
onClick={() => console.log(label)}
value={`${label.toLowerCase()}/?search=`}
/>
</label>
);
};
export default Checkbox;
and the Search component that implements checkboxes
import React, { Component } from 'react';
import Checkbox from './Checkbox';
const APIQuery = 'https://swapi.co/api/';
const searchLabels = ['Planets', 'Starships', 'People', 'Species', 'Films', 'Vehicles'];
export default class Searchbutton extends Component {
constructor(props) {
super(props);
this.state = {
endpointValue: '',
searchValue: '',
};
}
/* Funcionality to handle form and state of form */
/* Changes state of value whenever the form is changed, in realtime. */
handleChange(event) {
this.setState({ searchValue: event.target.value });
}
/* Prevents default formsubmit */
handleSubmit(event) {
event.preventDefault();
}
/* Handles state of checkboxes and sets state as to prepend necessary filter for request */
handleCheck(event) {
this.setState({ endpointValue: event.target.value });
if (this.state.endpointValue === event.target.value) {
this.setState({ endpointValue: '' });
}
}
/* Creates the checkboxes dynamically from the list of labels. */
createBoxes() {
const checkboxArray = [];
searchLabels.map(item => checkboxArray.push(
<Checkbox
key={item}
className="madeBoxes"
subKey={item}
endpointValue={this.state.endpointValue}
handleChange={e => this.handleChange(e)}
handleCheck={e => this.handleCheck(e)}
label={item}
/>,
));
return checkboxArray;
}
render() {
return (
<div className="search_content">
<div className="search_wrapper">
<form onSubmit={this.handleSubmit} method="#">
<label htmlFor="searchBar">
<input type="text" id="searchbar" className="search_bar" value={this.state.searchValue} onChange={e => this.handleChange(e)} />
</label>
<div>
<input type="submit" className="search_button" value="May the Force be with you." onClick={() => this.props.searchWithApi(APIQuery + this.state.endpointValue + this.state.searchValue)} />
</div>
</form>
</div>
<div className="checkboxes">
{this.createBoxes(this.labels)}
</div>
<div className="sort_filters">
{' '}
{/* These are options that the user can make in order to sort and filter the results.
The idea is to make it so that changing the value auto-perform a new request */}
{/* For sorting the returned objects based on user choice */}
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid, until href added */}
Choose sort method
<ul className="sorting">
<li className="sort_optn" href="#" value="lexicographical">Alphabetically</li>
<li className="sort_optn" href="#" value="by_added_date">By added date</li>
<li className="sort_optn" href="#" value="by_added_date_rev">By added date reversed</li>
</ul>
</div>
</div>
);
}
}
You don't really have to do it with react. You can reformat your code a little bit and solve it with CSS :checked pseudo-class.
In particular, don't wrap your checkbox within a label, but instead put the label after the input. Check this fiddle for example: https://jsfiddle.net/8c7a0fx5/
You can use the styled-component package. check the example below on how to use it:
import { Component } from 'react'
import { render } from 'react-dom'
import styled from 'styled-components'
const StyledCheckbox = styled.div`
label {
background: ${props => props.active ? 'red': 'white'}
}
`
class MyAwesomeComponent extends Component {
constructor(){
super()
this.state = {
isChecked: false
}
this.handleOnChange = this.handleOnChange.bind(this)
}
handleOnChange = ()=>{
this.setState({
isChecked: !this.state.isChecked,
})
}
render(){
const { isChecked } = this.state
return(
<StyledCheckbox active={isChecked}>
<label>Names</label>
<input type="checkbox" onChange={this.handleOnChange} />
</StyledCheckbox>
)
}
}
render(<MyAwesomeComponent/>, document.getElementById('root'))
Working code on codepen.io
I try to map the fetched data but I always get an error in my mapping because the data hasn't fetched before I use the map function. I'm able to get a get a specific element in from my fetched data using a click event.
parent class where I fetch my datas, I need the beer data for my mapping.
class App extends Component{
constructor(props){
super(props);
this.props.fetchUser();
this.props.fetchBeers();
}
class where I try to map my beers:
class BeersLanding extends Component{
getBeers = () => {
let beers= this.props.beers;
console.log(beers);
console.log(beers[0]);
console.log(beers[1]);
}
constructor(props){
super(props);
}
render(){
const {loading} =this.props;
console.log(this.props.beers)
return(
<div style={{textAlign:'center'}}>
...
<input type="text" placeholder="Search.." name="search"></input>
<button type="submit" onClick={() =>this.getBeers()} >Submit</button>
<div className={'beersContainer'}>
{this.props.beers.map((beer,index) =>(
<div className={'card'}>
hello
</div>
))}
</div>
</div>
);
}
}
Action method:
export const fetchBeers = () => async dispatch => {
const res = await axios.get('/api/beers');
dispatch({type:FETCH_BEERS, payload:res.data});
};
reducer:
export default function(state=null, action){
// console.log(action);
switch(action.type){
case FETCH_BEERS:
return action.payload;
default:
return state;
}
}
There are tow options to solve this issue, first option and I recommended to use this, by using defaultProps and set default value of bees as array.
the second option by add condition before map your data
{this.props.beers && this.props.beers.map((beer,index) =>(
<div className={'card'}>
hello
</div>
))}
I would recommend using react life cycle hooks for this type of issues.
componentDidMount() {
this.props.YOURACTIONS()
}
So this will happen when component is loaded.