React components not conditionally rendering? - javascript

I am trying to build a simple website that will display a random lyric after the user selects a musical artist. I am struggling with making components appear on react after the button is clicked. It is likely that this is a really simple issue given I just started working with React yesterday and learned JS a few days ago. My first screen renders with the two artists and a button to chose between them, but I can't get anything past that to pop up. I also seem to be having trouble with the states not updating.
import axios from 'axios';
import React, {Component} from 'react'
import taylorSwift from './taylorswift.jpg';
import kanyeWest from './kanyewest.jpeg';
const TAYLOR_ID = "259675";
const KANYE_ID = "33091467";
class Game extends Component {
constructor(props) {
super(props);
this.state = {
artist_id: "",
lyric: "",
actualTrack: "",
userTrack: "",
artistSelected: false
};
}
async getRandomLyric(artist_id) {
const response = await axios.get(`http://localhost:3000/randomlyric/:${artist_id}`);
this.setState({
lyric :response.lyric,
actualTrack: response.actualTrack});
}
choseArtist(artist_id) {
this.setState({
artist_id: artist_id,
artistSelected: true
})
return (
<div>
<p> Great! Now you a random lyric from the artist you chose will be presented and</p>
<p> you will have to guess which song this lyric is from. </p>
<p> Good luck! </p>
<div>
{this.getRandomLyric(artist_id)}
<p>What track is this lyric from: {this.state.lyric} </p>
<input type = "text" onChange ={ (e) => {
this.setState({userTrack: e.target.value})
}}/>
</div>
</div>
)
}
showLyrics (artist_id) {
}
render() {
return (
<div>
<h1>Welcome to the Song Guessing Game!</h1>
<p> These are the rules of the game: </p>
<p> First, you must pick whether you are Team Taylor or Team Kanye.</p>
<p> After you pick your team, you will be presented with 5 random lyrics from your chosen artist.</p>
<p> Your job is to guess which song each lyric is from. </p>
<p> Good luck!</p>
<div className = "team_taylor">
<div>
<img className = "artist_picture"
src={taylorSwift}
alt="Taylor Swift" />
</div>
<div className = "artist_button">
<button onClick={()=>{this.choseArtist(TAYLOR_ID); this.forceUpdate()}}> Team Taylor</button>
</div>
</div>
<div className = "team_kanye">
<div>
<img className = "artist_picture"
src={kanyeWest}
alt="Kanye West"/>
</div>
<div className = "artist_button">
<button onClick={()=>{this.choseArtist(KANYE_ID); this.forceUpdate()}}> Team Kanye</button>
</div>
</div>
</div>
);
}
}
export default Game;

If you just started learning react I recommend you to learn React functional components
and React hooks.
Here is an example of how your component would look like following the react functional components and hooks structure.
import { useEffect, useState } from "react";
const Game = () => {
const [artist, setArtist] = useState({
artist_id: "",
lyric: false,
actualTrack: "",
userTrack: "",
artistSelected: false
});
useEffect(() => {
artist.artistSelected && axios
.get(`http://localhost:3000/randomlyric/:${artist_id}`)
.then((response) =>
setArtist((prev) => ({
...prev,
lyric: response.lyric,
actualTrack: response.actualTrack
}))
)
.catch(error => console.log(error));
}, [artist.artistSelected]);
return artist.lyric ? (
<div>
<p>
{" "}
Great! Now you a random lyric from the artist you chose will be
presented and
</p>
<p> you will have to guess which song this lyric is from. </p>
<p> Good luck! </p>
<div>
<p>What track is this lyric from: {artist.lyric} </p>
<input
type="text"
onChange={(e) => {
setArtist(prev => ({...prev, userTrack:e.target.value}))
}}
/>
</div>
</div>
) : (
<div>
<h1>Welcome to the Song Guessing Game!</h1>
<p> These are the rules of the game: </p>
<p> First, you must pick whether you are Team Taylor or Team Kanye.</p>
<p>
{" "}
After you pick your team, you will be presented with 5 random lyrics
from your chosen artist.
</p>
<p> Your job is to guess which song each lyric is from. </p>
<p> Good luck!</p>
<div className="team_taylor">
<div>
<img
className="artist_picture"
src={taylorSwift}
alt="Taylor Swift"
/>
</div>
<div className="artist_button">
<button
onClick={() =>
setArtist((prev) => ({
...prev,
artist_id: TAYLOR_ID,
artistSelected: true
}))
}
>
{" "}
Team Taylor
</button>
</div>
</div>
<div className="team_kanye">
<div>
<img className="artist_picture" src={kanyeWest} alt="Kanye West" />
</div>
<div className="artist_button">
<button
onClick={() =>
setArtist((prev) => ({
...prev,
artist_id: KANYE_ID,
artistSelected: true
}))
}
>
{" "}
Team Kanye
</button>
</div>
</div>
</div>
);
};
export default Game;

Related

my react.js app is coming blank trying to seed with an external js file

I just started studying react, so I picked up the book Fullstack react by Anthony Accomozzio et al, with the latest version on January 13, 2020. I created the react app with npx create-react-app I am stuck on the part of 'making product data driven', I'm trying to seed data from an external file seed.js using props, but it returns a blank screen. But runs if data is hard encoded.
App.js:
import logo from './logo.svg';
import './App.css';
import './seed';
function App() {
const product = window.Seed.products[1];
return (
<div className="App">
<Product
id = {product.id}
title = {product.title}
decription = {product.description}
url = {product.url}
votes = {product.votes}
submitterAvatarUrl = {product.submitterAvatarUrl}
productImageUrl = {product.productImageUrl}
/>
</div>
);
}
const Product = () => {
return (
<div className = 'item'>
<div className = 'image'>
<img src = {this.props.productImageUrl} />
</div>
<div className = 'middle aligned content'>
<div className = 'header'>
<a>
<i className = 'large caret up icon' />
</a>
{this.props.votes}
</div>
<div className = 'description'>
<a href = {this.props.url}>
{this.props.title}
</a>
<p>
{this.props.description}
</p>
</div>
<div className='extra'>
<span>Submitted by:</span>
<img
className='ui avatar image'
src = {this.props.submitterAvatarUrl}
/>
</div>
</div>
</div>
);
}
export default App;
The seed.js that has the data:
window.Seed = (function (){
function generateVoteCount() {
return Math.floor((Math.random() * 50) + 15);
}
const products = [
{
id: 1,
title: 'Yellow Pail',
description: 'On-demand sand castle construction expertise.',
url: '#',
votes: generateVoteCount(),
submitterAvatarUrl: 'images/avatars/daniel.jpg',
productImageUrl: 'images/products/image-aqua.png',
},
...
];
return { products: products };
}());
Is the codes deprecated for the react version?
Your Product component is a functional component which means there is no this.props.
You'll need to take in the props as an argument and then you can use prop.xxx to access the values.
const Product = (props) => {
return (
<div className="item">
<div className="image">
<img src={props.productImageUrl} />
</div>
<div className="middle aligned content">
<div className="header">
<a>
<i className="large caret up icon" />
</a>
{props.votes}
</div>
<div className="description">
<a href={props.url}>{props.title}</a>
<p>{props.description}</p>
</div>
<div className="extra">
<span>Submitted by:</span>
<img className="ui avatar image" src={props.submitterAvatarUrl} />
</div>
</div>
</div>
);
};

How do i pass data values to modals using react hooks?

I am working on using modals to accept form inputs on a react project
here is the plan component
plan/index.js
import React, { useState } from "react";
import Pay from '../Pay';
const Plan = () => {
const [payModal, setPayModal] = useState(false);
const [planMode, setPlanMode] = useState(true);
return (
<main class="main">
{payModal && <Pay setOpenPayModal={setPayModal} />}
<div class="plan">
<div>
<a class="plans" onClick={() => {setPayModal(true);}}>plan A</a>
<div class="plan">
<span class="plan">{!planMode ? "$19.99" : "$9.99"}</span>
<span class="plan">{!planMode ? "month" : "year"}</span>
</div>
</div>
<div>
<a class="plans" onClick={() => {setPayModal(true);}}>plan B</a>
<div class="plan">
<span class="plan">{!planMode ? "$29.99" : "$19.99"}</span>
<span class="plan">{!planMode ? "month" : "year"}</span>
</div>
</div>
</div>
</main>
);
};
export default Plan;
as you can see on the line {payModal && <Pay setOpenPayModal={setPayModal} />} where i call the pay modal component from the plan component and it opens up the modal
here is the pay component which is the modal component
pay/index.js
import React, { useState } from "react";
function Pay({ setOpenPayModal }) {
const [billingDetails, setBillingDetails] = useState({
price: "",
: "",
});
return (
<div class="modal">
<div class="modal">
<div class="close">
<button
onClick={() => {
setOpenPayModal(false);
}}
>
</button>
</div>
<div class="modal">
<form class="form" onSubmit={handleSubmit}>
<fieldset class="form">
<Field
label="Price"
id="price"
type="text"
value={billingDetails.price}
onChange={(e) => {
setBillingDetails({ ...billingDetails, price: e.target.value });
}}
/>
<Field
label="Frequency"
id="frequency"
type="text"
value={billingDetails.frequency}
onChange={(e) => {
setBillingDetails({ ...billingDetails, frequency: e.target.value });
}}
/>
</fieldset>
<button class="pay" onClick={handleSubmitPay}>
Pay
</button>
</form>
</div>
</div>
</div>
);
}
export default Pay;
The issue is I want to be able to pass the values price and frequency from the plan component to the pay modal component
for example for plan A is price="$19.99" and frequency="year", so based on the button clicked on the plan component page, those values get passed to the pay modal component in a dynamic way
how do I achieve this using react hooks?
You can use contexts to pass, but in this case I don't think it's the best option. What I really recommend is passing the state variable through the props.
{payModal && <Pay setOpenPayModal={setPayModal} price={price} frequency={frequency} />}
I usually use the Context (useContext) when I need values and various components, for example:
I need to save the user id that is logged in to various components, and instead of getting it from the server every time I need it, I keep it saved in my context that I created, so I can make the call only once.
Documentation-> https://pt-br.reactjs.org/docs/context.html

excluding missing data when mapping through JSON in React/Js

I have a React component set up to map through a JSON file of projects and display the information in a card. However some of projects have less information that others. Namely my wordpress website does not need a link to the code. However I have it set up like:
Code:
<p>{project.code}</p>
How can I change this to an if statement, saying if Code is a property then return that block of code or else do not display 'code:' at all.
Here is my two files for reference.
Projects.js:
import React from "react";
import ReactCardFlip from "react-card-flip";
import Data from "../../ProjectData.json";
import './Projects.scss'
import '../MediaQueries/Projects.scss'
const CardStyle = {
padding: "30px",
margin: "30px",
width: "250px",
height: "300px",
};
const Card = ({ project }) => {
const [isFlipped, setIsFlipped] = React.useState(false);
// console.log(project);
return (
<div className="Card-wrapper">
<ReactCardFlip isFlipped={isFlipped} flipDirection="horizontal">
<div
style={CardStyle}
onMouseEnter={() => setIsFlipped((prev) => !prev)}
className="CardFront"
>
<div className="CardFront-div1">
<h3 className="CardFront-div1-title">{project.title}</h3>
<img width="250" src={project.gif} alt="" className="CardFront-div1-gif"/>
<div className="CardFront-div1-list">
<p>{project.html}</p>
<p>{project.css}</p>
<p>{project.javascript}</p>
<p>{project.react}</p>
</div>
</div>
</div>
<div
style={CardStyle}
onMouseLeave={() => setIsFlipped((prev) => !prev)}
className="CardBack"
>
<div>
<p>{project.description}</p>
<span>
Project:
<p>{project.link}</p>
</span>
<span>
Code:
<p>{project.code}</p>
</span>
</div>
</div>
</ReactCardFlip>
<button onClick={() => setIsFlipped((prev) => !prev)} className="cardflip-button">Flip</button>
</div>
);
};
const Projects = () => {
return (
<>
<h1>Projects</h1>
<div className="Projects" id="Projects">
{Data.map((item, index) => (
<Card project={item} key={`card-${index}`} />
))}
</div>
</>
);
};
export default Projects;
{
project.code ? (
<span>
Code:
<p>{project.code}</p>
</span>
) : null
}
Post got deleted by the user but the code is what I was looking for:
{!!project.code && (
<span >
Code:
<p>{project.code}</p>
</span>
)}

How to properly search in a list in ReactJS

I am trying to set a simple search operation in a user interface as shown below:
I have a total of 70 react-strap cards and each card contain a vessel with name, type and an image. I would like to search the name of the vessel and have the card related to that vessel to pop-up. All my images are currently contained inside the external database Contentful. Below the fields of interests:
The problem is that I don't know how to write a search function that locate a specific value of a list.
Below the code:
SideBar.js
import React from 'react';
import Client from '../Contentful';
import SearchVessel from '../components/SearchVessel';
class Sidebar extends React.Component {
state = {
ships: [],
};
async componentDidMount() {
let response = await Client.getEntries({
content_type: 'cards'
});
const ships = response.items.map((item) => {
const {
name,
slug,
type
} = item.fields;
return {
name,
slug,
type
};
});
this.setState({
ships
});
}
getFilteredShips = () => {
if (!this.props.activeShip) {
return this.state.ships;
}
let targetShip = this.state.ships.filter(
(ship) => this.props.activeShip.name === ship.name
);
let otherShipsArray = this.state.ships.filter((ship) => this.props.activeShip.name !== ship.name);
return targetShip.concat(otherShipsArray);
};
render() {
return (
<div className="map-sidebar">
{this.props.activeShipTypes}
<SearchVessel />
<pre>
{this.getFilteredShips().map((ship) => {
console.log(ship);
return (
<Card className="mb-2">
<CardImg />
<CardBody>
<div className="row">
<img
className="image-sizing-primary"
src={ship.companylogo.fields.file.url}
alt="shipImage"
/>
</div>
<div>
<img
className="image-sizing-secondary"
src={ship.images.fields.file.url}
alt="shipImage"
/>
</div>
<CardTitle>
<h3 className="thick">{ship.name}</h3>
</CardTitle>
<CardSubtitle>{ship.type}</CardSubtitle>
<CardText>
<br />
<h6>Project Details</h6>
<p>For a description of the project view the specification included</p>
</CardText>
<Row style={{ marginTop: '20px' }}>
<div className="buttoncontainer">
<div className="btn btn-cards">
<a
className="buttonLink"
download
href={ship.projectnotes.fields.file.url}
>
Project Notes
</a>
</div>
<div className="btn btn-cards">
<a className="buttonLink" href={ship.abstract.fields.file.url}>
Abstract
</a>
</div>
</div>
</Row>
</CardBody>
</Card>
);
})}
</pre>
</div>
);
}
}
export default Sidebar;
VesselSearch.js
import React, { Component } from 'react';
export default class SearchVessel extends Component {
render() {
const { value, handleSubmit, handleChange } = this.props;
return (
<React.Fragment>
<div className="container">
<div className="row">
<div className="col-10 mx-auto col-md-8 mt-5 text-center">
<h4 className="text-slanted text-capitalize">Search for Vessel</h4>
<form className="mt-4" onSubmit={handleSubmit}>
<label htmlFor="search" className="text-capitalize">
type vessel separated by comma
</label>
<div className="input-group">
<input
type="text"
name="search"
placeholder="Type name of vessel here"
className="form-control"
value={value}
onChange={handleChange}
/>
<div className="input-group-append">
<button type="submit" className="input-group-text bg-primary text-white">
<i className="fas fa-search" />
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</React.Fragment>
);
}
}
What I have done so far:
1) I tried different combination with the filter function and I think I am close. The problem is that when I operate the search nothing happens and in order to find the card of the vessel I want, I have to scroll down until I find it.
I am running out of ideas and if you see something I didn't catch point me in the right direction for solving this issue.
You're close! I would add a field to your state called 'searchText' and then create a method to filter based on that searchText state item.
getFilteredShips = () => this.state.ships.filter(s => s.name.includes(this.state.searchText)
Then just map over those values to render the cards that match the search text. The cards will update each time the searchText value updates.
this.getFilteredShips().map(ship => ..........
React is famous for re-usable component. You will have all the data of these vessels in an array. You will loop through the array and render the items with card component.And when you search for the specific card you want that vessel to pop out on top.
There are two ways to do it:
You have to run through the array, find the index of that vessel and do whatever it takes to manipulate your array and to make that item at top and re-render your list.
Alternatively render one more component on top of your vessel list as user clicks the search button. You just have to find the item index and render it. This way you don't have to deal with array manipulation. It doesn't matter if you have 80 or 1000 cards.
Please checkout official documentation for array methods, for array slicing and splice.
Hope this is what you are looking for. If you need further help, comment please.

Text input unfocused after one character React?

var uuid = require('uuid-v4');
// Generate a new UUID
var myUUID = uuid();
// Validate a UUID as proper V4 format
uuid.isUUID(myUUID); // true
var questionNum = 0;
class App extends Component {
constructor(props){
super(props);
this.state = {
key: uuid(),
title: "",
author: "",
questions: [],
answers: []
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
const target = event.target;
const value = target.type === "checkbox" ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
addQuestion = () => {
questionNum++;
this.setState({
questions: this.state.questions.concat(["question","hi"])
});
console.log(this.state.questions);
this.setState({
answers: this.state.answers.concat(["hello","hi"])
});
console.log(this.state.answers);
console.log(questionNum);
console.log(this.state.title);
console.log(this.state.author);
}
render() {
return (
<div className="App">
<div>
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Quiz Form 2.0</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
<div>
<form>
<div className="Intro">
Give your Quiz a title: <input type="text" value={this.state.title} onChange={this.handleChange} name="title" key={uuid()}/><br/>
Who's the Author? <input type="text" value={this.state.author} onChange={this.handleChange} name="author" key={uuid()}/><br/><br/>
</div>
<div className="questions">
Now let's add some questions... <br/>
{this.addQuestion}
</div>
</form>
<button onClick={this.addQuestion}>Add Question</button>
</div>
</div>
);
}
}
export default App;
Both of the inputs on my page unfocus after typing only one character. Here's my code, I'm not entirely sure where I am going wrong here, it all worked just fine yesterday.
If there is anything else that you need to know just leave a comment and I will get to it. Your help is much appreciated, thanks!
When the key given to a component is changed, the previous one is thrown away and a new component is mounted. You are creating a new key with uuid() every render, so each render a new input component is created.
Remove the key from your inputs and it will work as expected.
<div className="Intro">
Give your Quiz a title:
<input
type="text"
value={this.state.title}
onChange={this.handleChange}
name="title"
/>
<br/>
Who's the Author?
<input
type="text"
value={this.state.author}
onChange={this.handleChange}
name="author"
/>
<br/><br/>
</div>
Tholle's response is correct. However, you should make some adjustments to how you interact with state. I've reworked your code with comments and included the uuid fix.
class App extends Component {
constructor(props){
super(props);
this.state = {
title: "",
author: "",
questions: [],
answers: []
}
this.handleChange = this.handleChange.bind(this);
this.addQuestion = this.addQuestion.bind(this);
}
handleChange({ target }) {
// since you'll always have a synthetic event just destructure the target
const { checked, name, type, value } = target;
const val = type === 'checkbox' ? checked : value;
this.setState({
[name]: val
});
}
addQuestion() {
// never modify state directly -> copy state and modify
const { answers, questions } = Object.assign({}, this.state);
// update your answers
answers.push(["hello", "hi"]);
// update your questions
questions.push(["question", "hi"]);
// now update state
this.setState({
answers,
questions
}, () => {
console.log(this.state);
});
}
render() {
return (
<div className="App">
<div>
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Quiz Form 2.0</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
<div>
<form>
<div className="Intro">
Give your Quiz a title: <input type="text" value={this.state.title} onChange={this.handleChange} name="title" /><br/>
Who's the Author? <input type="text" value={this.state.author} onChange={this.handleChange} name="author" /><br/><br/>
</div>
<div className="questions">
Now let's add some questions... <br/>
{this.addQuestion}
</div>
</form>
<button onClick={this.addQuestion}>Add Question</button>
</div>
</div>
);
}
}
export default App;

Categories

Resources