Can't render image got from API inside React App - reactjs

I got list of images from an API, but none of them simply don't show up inside my app, it just hits the alt.. <img scr={this.state.randomImg} alt="Meme"></img> What-I-got-on-screen-after-hitting-"generete"-button-few-times.jpg although if I just paste same scr in inside the img in regular html file it renders perfectly
import React, { Component } from 'react';
class MemeGenerator extends Component {
constructor() {
super();
this.state = {
randomImg: "https://i.imgflip.com/1ur9b0.jpg",
question: "",
punchline: "",
allMemes: []
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.getRandomInt = this.getRandomInt.bind(this);
this.componentDidMount = this.componentDidMount.bind(this);
}
getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
componentDidMount() {
const imgFlip = "https://api.imgflip.com/get_memes";
fetch(imgFlip)
.then(response => response.json())
.then(response => {
const memes = response.data.memes;
this.setState(
{
allMemes: memes
}
)
this.setState({
randomImg: memes[this.getRandomInt(memes.length)].url
})
console.log(this.state.randomImg);
})
}
handleChange(event) {
const { name, value } = event.target;
this.setState(
{
[name]: value
}
)
}
handleSubmit(event) {
const number = this.getRandomInt(this.state.allMemes.length);
this.setState({
randomImg: this.state.allMemes[number].url
});
event.preventDefault();
console.log(this.state.randomImg);
}
render() {
return (
<main>
<form className='make-meme'>
<input type="text" name="question" value={this.state.question} onChange={this.handleChange} />
<input type="text" name="punchline" value={this.state.punchline} onChange={this.handleChange} />
<button type="submit" onClick={this.handleSubmit}>Genenerate</button>
</form>
<div className="imgWrapper">
<img scr={this.state.randomImg} alt="Meme"></img>
<h2 className="meme-text question">{this.state.question}</h2>
<h2 className="meme-text punchline">{this.state.punchline}</h2>
</div>
</main>
);
}
}
export default MemeGenerator;
My first question don't judge please

scr property of your image should be src
For Example:
you should have
<img src={this.state.randomImg} alt="Meme"></img>

Related

How do I use startsWith with a form submission when filtering array data from an API in React?

Sorry for the noob question, just starting out in React. So my page accesses a list of counties formatted as an Array from an API using this component:
class FetchRandomCounty extends React.Component {
state = {
loading: true,
county: null,
};
async componentDidMount() {
const url = "http://localhost:5000/api/counties";
const response = await fetch(url);
const data = await response.json();
this.setState({ county: data, loading: false });
}
render() {
return (
<div>
{this.state.loading || !this.state.county ? (
<div> loading... </div>
) : (
<div>
<div>
{" "}
{this.state.county
.filter((item) => item.startsWith("J"))
.map((item) => (
<li key={item}>{item}</li>
))}{" "}
</div>
</div>
)}
</div>
);
}
}
The fetched data is an Array that looks like this:
["Aransas", "Austin", "Bastrop", "Bee", "Brazoria", "Burleson", "Caldwell", "Calhoun", "Chambers", "Colorado", "Comal", "De Witt", "Fayette", "Fort Bend", "Galveston", "Goliad", "Gonzales", "Grimes", "Guadalupe", "Hardin", "Harris", "Jackson", "Jasper", "Jefferson", "Jim Wells"]
And the output currently is this:
Jackson
Jasper
Jefferson
Jim Wells
How do I prompt the user to enter a letter and filter the array to only display data that starts with that letter? Right now I am using item.startsWith('J')) and would like to make it respond to user input. This is my User input component:
class LetterForm 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 letter was submitted: " + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
StartsWith:
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
If you want to keep FetchRandomCounty and LetterForm as separate components, you could control the selected letter with a prop to FetchRandomCounty and control it from the parent, and introduce a onChange callback prop for LetterForm that you use to change the stored selected letter in the parent.
Example
const data = [
"Aransas",
"Austin",
"Bastrop",
"Bee",
"Brazoria",
"Burleson",
"Caldwell",
"Calhoun",
"Chambers",
"Colorado",
"Comal",
"De Witt",
"Fayette",
"Fort Bend",
"Galveston",
"Goliad",
"Gonzales",
"Grimes",
"Guadalupe",
"Hardin",
"Harris",
"Jackson",
"Jasper",
"Jefferson",
"Jim Wells"
];
class FetchRandomCounty extends React.Component {
state = {
loading: true,
county: null
};
componentDidMount() {
// const url = "http://localhost:5000/api/counties";
// const response = await fetch(url);
// const data = await response.json();
this.setState({ county: data, loading: false });
}
render() {
const { loading, county } = this.state;
const selectedLetter = this.props.selectedLetter.toLowerCase();
return (
<div>
{loading || !county ? (
<div> loading... </div>
) : (
<div>
<div>
{county
.filter((item) => item.toLowerCase().startsWith(selectedLetter))
.map((item) => (
<li key={item}>{item}</li>
))}
</div>
</div>
)}
</div>
);
}
}
class LetterForm extends React.Component {
state = { value: "" };
handleChange = (event) => {
this.setState({ value: event.target.value.slice(0, 1) });
};
handleSubmit = (event) => {
event.preventDefault();
this.props.onChange(this.state.value);
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
StartsWith:
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
class App extends React.Component {
state = { selectedLetter: "J" };
updateLetter = (selectedLetter) => {
this.setState({ selectedLetter });
};
render() {
return (
<div>
<FetchRandomCounty selectedLetter={this.state.selectedLetter} />
<LetterForm onChange={this.updateLetter} />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Deleting function not working and no errors appear

Good day so I have a question about firebase and perhaps my code as well I wrote some code in JSX and React linked to Firebase and the Button that I'm using to delete is not working properly.
I'm using Parent Child props to pass the function into the page that is needed to be deleted but there is no functionality. I need help thanks!
this is the parent where the function is located :
import React from 'react';
import fire from '../config/firebase';
import Modal from 'react-modal';
// import "firebase/database";
// import 'firebase/auth';
import NotesCard from './note-card';
Modal.setAppElement('#root');
export default class Notes extends React.Component {
_isMounted = false;
constructor(props) {
super(props);
this.state = {
notes: [],
showModal: false,
loggedin: false
};
this.handleOpenModal = this.handleOpenModal.bind(this);
this.handleCloseModal = this.handleCloseModal.bind(this);
this.handleAddNote = this.handleAddNote.bind(this);
this.handleRemoveNote = this.handleRemoveNote.bind(this);
}
componentDidMount() {
this._isMounted = true;
fire.auth().onAuthStateChanged((user) => {
if(user){
// call firebase from import fire
// grab userData and push it to the dataArray
fire.database().ref(`users/${user.uid}/notes`).on('value', (res) => {
const userData = res.val()
const dataArray = []
for(let objKey in userData) {
userData[objKey].key = objKey
dataArray.push(userData[objKey])
}
// set in the state
if(this._isMounted){
this.setState({
notes: dataArray,
loggedin: true
})
}
});
}else {
this.setState({loggedin: false})
}
});
};
componentWillUnmount() {
this._isMounted = false;
}
handleAddNote (e) {
e.preventDefault()
const note = {
title: this.noteTitle.value,
text: this.noteText.value
}
// reference where we can push it
const userId = fire.auth().currentUser.uid;
const dbRef = fire.database().ref(`users/${userId}/notes`);
dbRef.push(note)
this.noteTitle.value = ''
this.noteText.value = ''
this.handleCloseModal()
}
handleRemoveNote(key) {
const userId = fire.auth().currentUser.uid;
const dbRef = fire.database().ref(`users/${userId}/notes/${key}`);
dbRef.remove();
}
handleOpenModal (e) {
e.preventDefault();
this.setState({
showModal: true
});
}
handleCloseModal () {
this.setState({
showModal: false
});
}
render() {
return (
<div>
<button onClick={this.handleOpenModal}>create Note</button>
<section className='notes'>
{
this.state.notes.map((note, indx) => {
return (
<NotesCard
note={note}
key={`note-${indx}`}
handleRemoveNote={this.handleRemoveNote}
/>
)
}).reverse()
}
</section>
<Modal
isOpen={this.state.showModal}
onRequestClose={this.handleCloseModal}
shouldCloseOnOverlayClick={false}
style={
{
overlay: {
backgroundColor: '#9494b8'
},
content: {
color: '#669999'
}
}
}
>
<form onSubmit={this.handleAddNote}>
<h3>Add New Note</h3>
<label htmlFor='note-title'>Title:</label>
<input type='text' name='note-title' ref={ref => this.noteTitle = ref} />
<label htmlFor='note-text'>Note</label>
<textarea name='note-text' ref={ref => this.noteText = ref} placeholder='type notes here...' />
<input type='submit' onClick={this.handleAddNote} />
<button onClick={this.handleCloseModal}>close</button>
</form>
</Modal>
</div>
)
}
}
and this is where the function is being called :
import React from 'react';
import fire from '../config/firebase';
export default class NotesCard extends React.Component {
constructor(props) {
super(props);
this.state = {
editing: false,
note: {}
}
this.handleEditNote = this.handleEditNote.bind(this);
this.handleSaveNote = this.handleSaveNote.bind(this);
}
handleEditNote() {
this.setState({
editing: true
})
}
handleSaveNote(e) {
e.preventDefault()
const userId = fire.auth().currentUser.uid;
const dbRef = fire.database().ref(`users/${userId}/notes/${this.props.note.key}`);
dbRef.update({
title: this.noteTitle.value,
text: this.noteText.value
})
this.setState({
editing: false
})
}
render() {
let editingTemp = (
<span>
<h4>{this.props.note.title}</h4>
<p>{this.props.note.text}</p>
</span>
)
if(this.state.editing) {
editingTemp = (
<form onSubmit={this.handleSaveNote}>
<div>
<input
type='text'
defaultValue={this.props.note.title}
name='title'
ref={ref => this.noteTitle = ref}
/>
</div>
<div>
<input
type='text'
defaultValue={this.props.note.text}
name='text'
ref ={ref => this.noteText = ref}
/>
</div>
<input type='submit' value='done editing' />
</form>
)
}
return (
<div>
<button onClick={this.handleEditNote}>edit</button>
<button onClick={this.props.handleRemoveNote(this.state.note.key)}>delete</button>
{editingTemp}
</div>
)
}
}
Thank you in advance for taking a look at this code.
Second iteration answer
Working sandbox
Problem
looking at https://codesandbox.io/s/trusting-knuth-2og8e?file=/src/components/note-card.js:1621-1708
I see that you have this line
<button onClick={()=> this.props.handleRemoveNote(this.state.note.key)}>delete
Yet your state.note declared as an empty map in the constructor:
this.state = {
editing: false,
note: {}
}
But never assigned a value using this.setState in the component
Solution
Change it to:
<button onClick={()=> this.props.handleRemoveNote(**this.props.note.key**)}>delete</button>
First iteration answer
NotesCard's buttons is firing the onClick callback on render instead on click event.
This is because you have executed the function instead of passing a callback to the onClick handler
Change
<button onClick={this.props.handleRemoveNote(this.state.note.key)}>delete</button>
To
<button onClick={()=> this.props.handleRemoveNote(this.state.note.key)}>delete</button>

heroku cannot find my cloudinary environment variables

I have a student who has an app that will host a photo to cloudinary then store it in his database. Everything worked fine locally but when he tried to host his app on heroku his app couldn't find his cloudinary environment variables. As far as I know he has done everything right and his app can find his other variables just not the cloudinary ones. Has anyone run into a problem similar to this and can give me any direction?
import React, { Component } from 'react';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import Dropzone from 'react-dropzone';
import ReactQuill from 'react-quill';
import request from 'superagent';
const CLOUDINARY_UPLOAD_PRESET = process.env.CLOUDINARY_UPLOAD_PRESET
const CLOUDINARY_UPLOAD_URL = process.env.CLOUDINARY_UPLOAD_URL
export default class UpdateBook extends Component {
constructor(props){
super(props);
this.state={
id: "",
title: "",
blog_status: "",
content: "",
featured_image_url: "",
formHidden: true,
uploadedFile:'',
uploadedFileCloudinaryUrl: '',
editMode: false
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.editBlog = this.editBlog.bind(this)
this.handleContentChange = this.handleContentChange.bind(this)
this.handleImageDrop = this.handleImageDrop.bind(this)
this.componentConfig=this.componentConfig.bind(this);
this.djsConfig=this.djsConfig.bind(this);
this.deleteImage = this.deleteImage.bind(this);
}
handleChange(event){
this.setState({
[event.target.name]: event.target.value
})
}
handleContentChange(value) {
this.setState({ content: value })
}
handleSubmit(event){
let id = this.state.id
let title = this.state.title;
let blog_status = this.state.blog_status;
let content = this.state.content;
let featured_image_url = this.state.featured_image_url;
fetch(`https://rwtw-backend-tw.herokuapp.com/update_blog/${id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({title: title, blog_status: blog_status, content: content, featured_image_url: featured_image_url})
}).then(response =>{
response.json()
}).then(responseData =>{
return responseData
}).catch(error => console.log("Fetch error", error))
}
handleImageDrop(files){
this.setState({
uploadedFile: files[0]
})
const currentFile = files[0]
const myFileItemReader = new FileReader()
myFileItemReader.addEventListener("load", () => {
this.setState({
image_src: myFileItemReader.result
})
}, false)
myFileItemReader.readAsDataURL(currentFile)
let upload = request.post(CLOUDINARY_UPLOAD_URL)
.field("upload_preset", CLOUDINARY_UPLOAD_PRESET)
.field("file", this.state.uploadedFile);
upload.end((err, response) => {
if (err){
console.log(err)
} if (response.body.secure_url !== ""){
this.setState({
featured_image_url: response.body.secure_url
})
}
})
}
editBlog(){
this.setState({editMode: true})
this.setState({id: this.props.ourProp[0]})
this.setState({title: this.props.ourProp[1]})
this.setState({blog_status: this.props.ourProp[2]})
this.setState({content: this.props.ourProp[3]})
this.setState({featured_image_url: this.props.ourProp[4]})
}
componentConfig(){
return{
iconFileTypes: [".jpg", ".png"],
showFiletypeIcon: true,
postUrl: 'https://httpbin.org/post'
}
}
djsConfig(){
return{
addRemoveLinks: true,
maxFiles: 1
}
}
deleteImage(){
this.setState({
featured_image_url: ""
})
}
render(){
return (
<div>
{this.state.editMode ?
<form>
<div className="blog-manager-title">
<input
type="text"
name="title"
placeholder="Blog Title"
value = {this.state.title}
onChange = {this.handleChange}
/>
</div>
<div className="blog-manager-blog_status">
<select
type="text"
name="blog_status"
value = {this.state.blog_status}
onChange = {this.handleChange}
>
<option value="draft">Draft</option>
<option value="published">Published</option>
</select>
</div>
<div className="blog-manager-content">
<ReactQuill
value={this.state.content}
onChange={this.handleContentChange}
/>
</div>
<div>
{this.state.featured_image_url !== "" ?
<div>
<img src={this.state.featured_image_url} style={{width: '400px'}}/>
<div className="image-removal-link">
<a onClick={() => this.deleteImage()}>Remove Image</a>
</div>
</div > :
<Dropzone
onDrop={this.handleImageDrop}
multiple= {false}
accept="image/*"
>
Add a picture
</Dropzone>
}
</div>
<div className="submit">
<button onClick={this.handleSubmit} value="submit">Add Blog</button>
</div>
</form> : null}
<button onClick={this.editBlog}><FontAwesomeIcon icon="edit"/></button>
</div>
);
}
}
In Heroku, there is a Cloudinary add-on. This will create a new Cloudinary account. So if your student create Cloudinary account outside Heroku, this will be 2 different account.
To double check this, from their Heroku account, try to login to Cloudinary, check if it's using the same cloud name.

how to make parent component pass prop to children only when button is clicked

I want to pass props from AddPost component to AllPosts component only when button is clicked in AddPost.
Plus how to keep on adding new posts' data(post,title,keyid) from AddPost in a object "newArray" in AllPosts every time button is clicked and this new data gets saved in allposts array and then every post is displayed by applying map function on it.
I am facing problem about how can I get new data from AddPost in newObject and continuously keep pushing this in allposts array?
AddPost.js
class Addpost extends Component {
constructor(props) {
super(props);
this.state = {
title : '',
post : '',
keyid : 0
}
this.handleClick = this.handleClick.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
[event.target.name] : event.target.value
})
}
handleClick() {
this.setState(prevState => ({
keyid : prevState.keyid + 1,
post : '',
title : ''
}));
console.log(this.state.keyid);
}
render() {
return(
<div>
<input type="text" name="title" value={this.state.title} onChange={this.handleChange} maxLength="30" placeholder="Title here" />
<input type="text" name="post" value={this.state.post} onChange={this.handleChange} maxLength="200" placeholder="Post here" />
<input type="button" onClick={this.handleClick} value="Add Post" />
<Allposts post={this.state.post} title={this.state.title} keyid={this.state.keyid} />
</div>
)
}
}
AllPosts.js
class Allposts extends Component {
constructor(props) {
super();
this.state = {
newObject : {
post : '',
title : '',
keyid : ''
},
allPosts : []
}
}
render() {
return (
this.state.allPosts.map((post) =><div>
{ post.post}{post.title}{post.keyid}
</div>
)
)
}
}
A better way to solve your problem would be to keep AllPosts and Addpost component isolated and rendered by their component Parents
post.js
class Post extends React.Component {
state: {
allPosts: []
}
addPost = (post) => {
this.setState(prev => ({allPosts: prev.allPosts.concat([post])}))
}
render() {
<>
<Addpost addPost={this.addPost}/>
<AllPosts allPosts={this.state.allPosts} />
</>
}
}
Addpost.js
class Addpost extends Component {
constructor(props) {
super(props);
this.state = {
title : '',
post : '',
keyid : 0
}
this.handleClick = this.handleClick.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
[event.target.name] : event.target.value
})
}
handleClick() {
const { keyid, post, title } = this.state;
const post = { keyid, post, title };
this.props.addPost(post)
this.setState(prevState => ({
keyid : prevState.keyid + 1,
post : '',
title : ''
}));
}
render() {
return(
<div>
<input type="text" name="title" value={this.state.title} onChange={this.handleChange} maxLength="30" placeholder="Title here" />
<input type="text" name="post" value={this.state.post} onChange={this.handleChange} maxLength="200" placeholder="Post here" />
<input type="button" onClick={this.handleClick} value="Add Post" />
</div>
)
}
}
Allposts.js
const Allposts = () => {
return (
this.props.allPosts.map((post) => (
<div>
{ post.post} {post.title} {post.keyid}
</div>
))
)
}
However if you want to pass props only after clicking, you would need to maintain a state that says clicked or not. and then pass props like
const { clicked, post, keyid, title } = this.state;
const newProp = { post, keyid, title };
<AllPost {...(clicked? newProps: {})} />

update values in react form input fields

I am new to react and I can fetch the result from form input fields. Now I need to update those values and submit to the backend. I am struggling to find a way to pass all the input field values at once.
constructor(props) {
super(props);
this.state = {
items: [],
isLoaded: false,
data: this.props.location.data
};
}
render() {
return (
<div>
<h2>Update Your Profile</h2>
{items.map(item => (
<Form key={item.uId} onSubmit={this.handleSubmit}>
<label>User Name</label>
<input type="text" defaultValue={item.userName}></input>
<label>Email address</label>
<input type="email" defaultValue={item.email}></input>
</div>
<button type="submit" >Update</button>
</Form>
))}
</div>
);
}
handleSubmit = (e) => {
e.preventDefault();
axios.put('http://localhost:3000/api/user/' + this.state.data, this.state.items).then(response => {
//
});
};
My API call looks like this:
app.put('/api/user/:userId', (req, res, err) => {
User.update(
{ userName: req.body.userName, email: req.body.email },
{
where: {
userId: req.params.userId
}
}
).then(function (rowsUpdated) {
res.json(rowsUpdated)
}).catch(err);
});
How can I modify this code to set a value for this.state.items with all the updated fields values and submit it?
I'd recommend to create a new component to wrap around the <Form /> and move the submit/change event handling to that component for each item. This would allow you to be able to extract individual email/userName for any given <Form /> to send as a PUT to your API endpoint as well as handle the respective input value changes.
Parent Component:
class Parent extends Component {
constructor() {
super();
this.state = {
name: 'React',
items: [
{ uId: 1, email: 'foo#test.com', userName: 'bar' },
{ uId: 2, email: 'baz#test.com', userName: 'foobar' }
]
};
}
render() {
return (
<div>
{this.state.items.map(item =>
<MyForm key={item.uId} item={item} data={this.props.location.data} />)}
</div>
);
}
}
Child/Form Component:
import React, { Component } from 'react';
class MyForm extends Component {
constructor(props) {
super(props);
this.state = {
email: this.props.item.email,
userName: this.props.item.userName
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
// https://reactjs.org/docs/forms.html#handling-multiple-inputs
handleChange(e) {
const { target} = event;
const value = target.type === 'checkbox' ? target.checked : target.value;
const { name } = target;
this.setState({ [name]: value });
}
handleSubmit(e) {
e.preventDefault();
const { email, userName } = this.state;
const body = { email, userName };
const json = JSON.stringify(body);
console.log(json);
// axios.put('http://localhost:3000/api/user/' + this.props.data, json).then(response => {});
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>User Name</label>
<input type="text" defaultValue={this.state.userName}></input>
<label>Email address</label>
<input type="email" defaultValue={this.state.email}></input>
<button type="submit" >Update</button>
</form>
);
}
}
export default MyForm;
Here is an example in action.
Hopefully that helps!

Resources