I'm still new to React.
I'm making a guessing game. On page load, everything loads properly (on Chrome and Safari, at least). The cat buttons are assigned a random number and when clicked, they send the corresponding value to the game logic. When the target number is met or exceeded, the target number resets.
That's what I want, but I also want the buttons to get new values. I want the Buttons component to reload and assign the buttons new values. I've tried using the updating methods found here: https://reactjs.org/docs/react-component.html#updating. I don't know what to do next.
App.js
import React, { Component } from 'react';
import './App.css';
import Buttons from "./components/Buttons/Buttons";
class App extends Component {
targetNumber = (min, max) => {
const targetNum = Math.floor(Math.random()*(max-min+1)+min);
console.log(`Target number = ${targetNum}`);
return targetNum
};
state = {
targetNumber: this.targetNumber(19, 120),
currentValue: 0,
gamesWon: 0,
};
handleClick = (event) => {
event.preventDefault();
const currentValue = this.state.currentValue;
const newValue = parseInt(event.target.getAttribute("value"));
this.setState(
{currentValue: currentValue + newValue}
)
// console.log(newValue);
}
componentDidUpdate() {
if (this.state.currentValue === this.state.targetNumber) {
this.setState(
{
targetNumber: this.targetNumber(19, 120),
currentValue: 0,
gamesWon: this.state.gamesWon + 1
}
)
}
else {
if (this.state.currentValue >= this.state.targetNumber) {
this.setState(
{
targetNumber: this.targetNumber(19, 120),
currentValue: 0,
gamesWon: this.state.gamesWon,
}
);
}
}
}
render() {
return (
<div className="App">
<img src={require("./images/frame.png")} alt="frame" id="instructFrame" />
<div className="resultsDiv">
<div className="targetNumber">
Target number = {this.state.targetNumber}
</div>
<div className="currentValue">
Current value = {this.state.currentValue}
</div>
<div className="gamesWon">
Games won = {this.state.gamesWon}
</div>
</div>
<div className="buttonGrid">
<Buttons
onClick={this.handleClick}
/>
</div>
</div>
);
}
}
export default App;
Buttons.js
import React, { Component } from "react";
import Button from "../Button/Button";
import black from "../Button/images/black_cat.png";
import brown from "../Button/images/brown_cat.png";
import gray from "../Button/images/gray_cat.png";
import yellow from "../Button/images/yellow_cat.png";
class Buttons extends Component {
generateNumber = (min, max) => {
const rndNumBtn = Math.floor(Math.random()*(max-min+1)+min);
console.log(rndNumBtn);
return rndNumBtn
};
state = {
buttons: [
{
id: "black",
src: black,
alt: "blackBtn",
value: this.generateNumber(1, 12)
},
{
id: "brown",
src: brown,
alt: "brownBtn",
value: this.generateNumber(1, 12)
},
{
id: "gray",
src: gray,
alt: "grayBtn",
value: this.generateNumber(1, 12)
},
{
id: "yellow",
src: yellow,
alt: "yellowBtn",
value: this.generateNumber(1, 12)
}
]
};
render() {
return (
<div>
{this.state.buttons.map(button => {
return (
<Button
className={button.id}
key={button.id}
src={button.src}
alt={button.alt}
value={button.value}
onClick={this.props.onClick.bind(this)}
/>
)
})}
</div>
)
}
}
export default Buttons;
Here's the GitHub repo. https://github.com/irene-rojas/numberguess-react
You can add a key to the Button component linking to the variable targetNumber. That way, React would rerender the Button whenever targetNumber changes.
<div className="buttonGrid">
<Buttons
key={this.state.targetNumber}
onClick={this.handleClick}
/>
</div>
Related
Hi i want to create a dropDown in react with each item having an icon. As i tried react-select but its not showing the icon ,and also the selected value .When i remove value prop from react-select component than the label is showing. I want to create the dropdown like the this.
//USerInfo.js
import React from "react";
import { connect } from "react-redux";
import FontAwesome from "react-fontawesome";
import Select from "react-select";
import { setPresence } from "../../ducks/user";
import "./UserInfo.css";
class UserInfo extends React.Component {
// constructor(props) {
// super(props);
// this.state = {
// currentPresence: "available",
// };
// }
presenceOpts = [
{ value: "available", label: "Available" },
{ value: "dnd", label: "Busy" },
{ value: "away", label: "Away" },
];
setPresenceFun(presence) {
this.props.setPresence(presence);
}
renderPresenceOption(option) {
return (
<div className="presenceOption">
<FontAwesome name="circle" className={"presenceIcon " + option.value} />
{option.label}
</div>
);
}
renderPresenceValue(presence) {
return (
<div className="currentPresence">
<FontAwesome
name="circle"
className={"presenceIcon " + presence.value}
/>
</div>
);
}
render() {
return (
<div className="UserInfo">
{this.props.client.authenticated && (
<div className="authenticated">
<div className="user">
<div className="presenceControl">
<Select
name="presence"
value={this.props.user.presence.value}
options={this.presenceOpts}
onChange={this.setPresenceFun.bind(this)}
clearable={false}
optionRenderer={this.renderPresenceOption}
valueRenderer={this.renderPresenceValue}
/>
</div>
<div className="username">
<p>{this.props.client.jid.local}</p>
</div>
</div>
</div>
)}
</div>
);
}
}
const mapStateToProps = (state, props) => ({
client: state.client,
user: state.user,
});
const mapDispatchToProps = (dispatch, props) => {
return {
setPresence: (presence) => dispatch(setPresence(presence)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(UserInfo);
You can customize the option for dropdown
This may assist you in resolving the custom styling issue with react select.
https://codesandbox.io/s/react-select-add-custom-option-forked-u1iee?file=/src/index.js
I use react js to create a staycation website, when I want to display the InputNumber and InputDate components I experience an error like the title above, in the componentDidUpdate section, I have tried tweaking the code but it hasn't worked, but when I omit the componentDidUpdate part, the inputdate and inputnumber components run.
this is the input component code Number I have tried the input component works well,:
import React from "react";
import propTypes from "prop-types";
import "./index.scss";
export default function Number(props) {
const {
value,
placeholder,
name,
min,
max,
prefix,
suffix,
isSuffixPlural,
} = props;
const onChange = (e) => {
let value = String(e.target.value);
if (+value <= max && +value >= min) {
props.onChange({
target: {
name: name,
value: +value,
},
});
}
};
const minus = () => {
value > min &&
onChange({
target: {
name: name,
value: +value - 1,
},
});
};
const plus = () => {
value < max &&
onChange({
target: {
name: name,
value: +value + 1,
},
});
};
return (
<div className={["input-number mb-3", props.outerClassName].join(" ")}>
<div className="input-group">
<div className="input-group-prepend">
<span className="input-group-text minus" onClick={minus}>
-
</span>
</div>
<input
min={min}
max={max}
name={name}
pattern="[0-9]*"
className="form-control"
placeholder={placeholder ? placeholder : "0"}
value={`${prefix}${value}${suffix}${
isSuffixPlural && value > 1 ? "s" : ""
}`}
onChange={onChange}
/>
<div className="input-group-append">
<span className="input-group-text plus" onClick={plus}>
+
</span>
</div>
</div>
</div>
);
}
Number.defaultProps = {
min: 1,
max: 1,
prefix: "",
suffix: "",
};
Number.propTypes = {
value: propTypes.oneOfType([propTypes.string, propTypes.number]),
onChange: propTypes.func,
placeholder: propTypes.string,
isSuffixPlural: propTypes.bool,
outerClassName: propTypes.string,
};
and this is my input date component code I have tried the input component works well, :
import React, { useState, useRef, useEffect } from "react";
import propTypes from "prop-types";
import { DateRange } from "react-date-range";
import "./index.scss";
import "react-date-range/dist/styles.css"; // main css file
import "react-date-range/dist/theme/default.css"; // theme css file
import formatDate from "utils/formatDate";
import iconCalendar from "assets/images/icon/icon-calendar.svg";
export default function Date(props) {
const { value, placeholder, name } = props;
const [isShowed, setIsShowed] = useState(false);
const datePickerChange = (value) => {
const target = {
target: {
value: value.selection,
name: name,
},
};
props.onChange(target);
};
useEffect(() => {
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
});
const refDate = useRef(null);
const handleClickOutside = (event) => {
if (refDate && !refDate.current.contains(event.target)) {
setIsShowed(false);
}
};
const check = (focus) => {
focus.indexOf(1) < 0 && setIsShowed(false);
};
const displayDate = `${value.startDate ? formatDate(value.startDate) : ""}${
value.endDate ? " - " + formatDate(value.endDate) : ""
}`;
return (
<div
ref={refDate}
className={["input-date mb-3", props.outerClassName].join(" ")}
>
<div className="input-group">
<div className="input-group-prepend bg-gray-900">
<span className="input-group-text">
<img src={iconCalendar} alt="icon calendar" />
</span>
</div>
<input
readOnly
type="text"
className="form-control"
value={displayDate}
placeholder={placeholder}
onClick={() => setIsShowed(!isShowed)}
/>
{isShowed && (
<div className="date-range-wrapper">
<DateRange
editableDateInputs={true}
onChange={datePickerChange}
moveRangeOnFirstSelection={false}
onRangeFocusChange={check}
ranges={[value]}
/>
</div>
)}
</div>
</div>
);
}
Date.propTypes = {
value: propTypes.object,
onChange: propTypes.func,
placeholder: propTypes.string,
outerClassName: propTypes.string,
};
I have tried the inpudate component to run well, as well as the input number, but if I combine these components I have an error did i miss something, and I tried to combine these components on the bookingform page but when I tried on the browser I experienced the above error.
My code is in the Booking Form:
import React, { Component } from "react";
import propTypes from "prop-types";
import Button from "elements/Button";
import { InputNumber, InputDate } from "elements/Form";
export default class BookingForm extends Component {
constructor(props) {
super(props);
this.state = {
data: {
duration: 1,
date: {
startDate: new Date(),
endDate: new Date(),
key: "selection",
},
},
};
}
updateData = (e) => {
this.setState({
...this.state,
data: {
...this.state.data,
[e.target.name]: e.target.value,
},
});
};
componentDidUpdate(prevProps, prevState) {
const { data } = this.state;
if (prevState.data.date !== data.date) {
const startDate = new Date(data.date.startDate);
const endDate = new Date(data.date.endDate);
const countDuration = new Date(endDate - startDate).getDate();
this.setState({
data: {
...this.state.data,
duration: countDuration,
},
});
}
if (prevState.data.duration !== data.duration) {
const startDate = new Date(data.date.startDate);
const endDate = new Date(
startDate.setDate(startDate.getDate() + +data.duration - 1)
);
this.setState({
...this.state,
data: {
...this.state.data,
date: {
...this.state.data.date,
endDate: endDate,
},
},
});
}
}
startBooking = () => {
const { data } = this.state;
this.props.startBooking({
_id: this.props.itemDetails._id,
duration: data.duration,
date: {
startDate: data.date.startDate,
endDate: data.date.endDate,
},
});
this.props.history.push("/checkout");
};
render() {
const { data } = this.state;
const { itemDetails } = this.props;
console.log(this.state);
return (
<div className="card bordered" style={{ padding: "60px 80px" }}>
<h4 className="mb-3">Start Booking</h4>
<h5 className="h2 text-teal mb-4">
${itemDetails.price}{" "}
<span className="text-gray-500 font-weight-light">
per {itemDetails.unit}
</span>
</h5>
<label htmlFor="duration">How long you will stay?</label>
<InputNumber
max={30}
suffix={" night"}
isSuffixPlural
onChange={this.updateData}
name="duration"
value={data.duration}
/>
<label htmlFor="date">Pick a date</label>
<InputDate onChange={this.updateData} name="date" value={data.date} />
<h6
className="text-gray-500 font-weight-light"
style={{ marginBottom: 40 }}
>
You will pay{" "}
<span className="text-gray-900">
${itemDetails.price * data.duration} USD
</span>{" "}
per{" "}
<span className="text-gray-900">
{data.duration} {itemDetails.unit}
</span>
</h6>
<Button
className="btn"
hasShadow
isPrimary
isBlock
onClick={this.startBooking}
>
Continue to Book
</Button>
</div>
);
}
}
BookingForm.propTypes = {
itemDetails: propTypes.object,
startBooking: propTypes.func,
};
I encountered this error and tried to fix it, but couldn't find a solution to the problem
I use react js to create a staycation website, when I want to display the InputNumber and InputDate components I experience an error like the title above, in the componentDidUpdate section, I have tried tweaking the code but it hasn't worked, but when I omit the componentDidUpdate part, the inputdate and inputnumber components run.
I encountered this error and tried to fix it, but couldn't find a solution to the problem
I use react js to create a staycation website, when I want to display the InputNumber and InputDate components I experience an error like the title above, in the componentDidUpdate section, I have tried tweaking the code but it hasn't worked, but when I omit the componentDidUpdate part, the inputdate and inputnumber components run.
I encountered this error and tried to fix it, but couldn't find a solution to the problem
I use react js to create a staycation website, when I want to display the InputNumber and InputDate components I experience an error like the title above, in the componentDidUpdate section, I have tried tweaking the code but it hasn't worked, but when I omit the componentDidUpdate part, the inputdate and inputnumber components run.
I'm follow the steps of this dependencie:
http://jossmac.github.io/react-images/
And it isn't work. No picture showing and there is showing an error message:
index.js:2178 Warning: Failed prop type: The prop onClose is marked
as required in Lightbox, but its value is undefined
Here is my code:
import React, { Component } from "react";
import Lightbox from "react-images";
const URL_INTERIORES = "http://localhost:3001/interiores";
const LIGHTBOX_IMAGE_SET = [
{
src: "/images/int_02.jpg",
caption: "A forest",
// As an array
srcSet: ["/images/int_02.jpg", "/images/int_02.jpg"]
},
{
src: "/images/int_02.jpg",
// As a string
srcSet: "/images/int_02.jpg 1024w, /images/int_02.jpg 800w, /images/int_02.jpg 500w, /images/int_02.jpg 320w"
}
];
class Interiores extends Component {
render() {
const { open } = this.state;
return (
<div>
<div>
<Lightbox
images={LIGHTBOX_IMAGE_SET}
isOpen={this.state.lightboxIsOpen}
onClickPrev={this.gotoPrevLightboxImage}
onClickNext={this.gotoNextLightboxImage}
onClose={this.closeLightbox}
/>
</div>
</div>
);
}
}
export default Interiores;
Does anybody know how to solve it? Tahnk you
Consider adding all the missing handlers & state in your class:
class Interiores extends Component {
state = {
lightboxIsOpen: false
}
gotoPrevLightboxImage() {
// Add the logic here
}
gotoNextLightboxImage() {
// Add the logic here
}
closeLightbox(e) {
// Add the logic here
}
render() {
const { lightboxIsOpen } = this.state;
return (
<div>
<Lightbox
images={LIGHTBOX_IMAGE_SET}
isOpen={lightboxIsOpen}
onClickPrev={() => this.gotoPrevLightboxImage()}
onClickNext={() => this.gotoNextLightboxImage()}
onClose={e => this.closeLightbox(e)}
/>
</div>
);
}
}
I am making a file sharing web app using a MERN development stack.
While connecting my file sharing screen to my updating screen, I got stuck.
ComponentsWillReceiveProps() is not working in the current version of React. I tried to find an alternative and it showed either set
UNSAFE_ComponentsWillReceiveProps() or use the function static getDerivedStateFromProps(nextProps, prevState), but I don't know how to define prevState.
My code is:
import React,{Component} from 'react'
import _ from "lodash"
import PropTypes from 'prop-types'
class HomeUploading extends Component{
constructor(props) {
super(props);
this.state = {
data: null,
event: null,
loaded: 0,
total: 0,
percentage: 10,
}
}
componentDidMount() {
const {data} = this.props;
this.setState({
data: data
});
}
static getDerivedStateFromProps(nextProps,prevState){
const {event} = nextProps;
console.log("Getting an event of uploading", event,prevState);
switch (_.get(event, 'type')) {
case 'onUploadProgress' :
const loaded = _.get(event, 'payload.loaded', 0);
const total = _.get(event, 'payload.total', 0);
const percentage = (total !== 0) ? ((loaded / total) * 100) : 0;
this.setState ({
loaded: loaded,
total: total,
percentage: percentage
});
break;
default:
break;
}
this.setState({
event:event,
}) ;
}
render() {
const {percentage, data, total, loaded} = this.state;
const totalFiles = _.get(data, 'files', []).length;
return (
<div className={'app-card app-card-uploading'}>
<div className={'app-card-content'}>
<div className={'app-card-content-inner'}>
<div className={'app-home-uploading'}>
<div className={'app-home-uploading-icon'}>
<i className={'icon-cloud-upload'}/>
<h2>Sending...</h2>
</div>
<div className={'app-upload-files-total'}>Uploading {totalFiles} files</div>
<div className={'app-progress'}>
<span style={{width: `${percentage}%`}} className={'app-progress-bar'}/>
</div>
<div className={'app-upload-stats'}>
<div className={'app-upload-stats-left'}>{loaded}Bytes/{total}Bytes</div>
<div className={'app-upload-stats-right'}>456K/s</div>
</div>
<div className={'app-form-actions'}>
<button className={'app-upload-cancel-button app-button'} type={'button'}>Cancel
</button>
</div>
</div>
</div>
</div>
</div>
)
}
}
HomeUploading.propTypes={
data: PropTypes.object,
event: PropTypes.object
}
export default HomeUploading;
Just glancing at the code, I'd refactor this a bit to keep this component as simple as possible (dumb/presentational component) that just displays new props as they're fed in.
Here's a description :https://medium.com/#thejasonfile/dumb-components-and-smart-components-e7b33a698d43
I'd suggest lifting the state up a level and doing the data transformation in the parent.
Something like this for the HomeUploading:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
const HomeUploading = ({
data,
event,
loaded,
total,
percentage,
}) => (
<div className={'app-card app-card-uploading'}>
<div className={'app-card-content'}>
<div className={'app-card-content-inner'}>
<div className={'app-home-uploading'}>
<div className={'app-home-uploading-icon'}>
<i className={'icon-cloud-upload'} />
<h2>Sending...</h2>
</div>
<div className={'app-upload-files-total'}>Uploading {data.length} files</div>
<div className={'app-progress'}>
<span style={{ width: `${percentage}%` }} className={'app-progress-bar'} />
</div>
<div className={'app-upload-stats'}>
<div className={'app-upload-stats-left'}>
{loaded}Bytes/{total}Bytes
</div>
<div className={'app-upload-stats-right'}>456K/s</div>
</div>
<div className={'app-form-actions'}>
<button className={'app-upload-cancel-button app-button'} type={'button'}>
Cancel
</button>
</div>
</div>
</div>
</div>
</div>
);
HomeUploading.propTypes = {
data: PropTypes.object,
event: PropTypes.object,
};
export default HomeUploading;
And then in the parent, you could have a the logic for transforming the data in a function:
import React, { Component } from 'react';
class Parent extends Component {
state = {
data: null,
event: null,
loaded: 0,
total: 0,
percentage: 0,
}
calcPercentage(loaded, total) {
return (total !== 0) ? ((loaded / total) * 100) : 0
}
render() {
const {
data,
event,
loaded,
total
} from this.state;
return (
<HomeUploading
data={data}
event={event}
loaded={loaded}
total={total}
percentage={this.calcPercentage(loaded, total)}
/>
);
}
}
export default Parent;
This approach will give you the props updating without relying on componentWillReceiveProps.
I'm trying to load items from JSON and toggle a dropdown div with description on click. While I can display elements sequentially (ex: loc1 & desc1, loc2 & desc2) on static divs I'm having trouble finding out how to render it properly when the second part (desc) is hidden and only shows when the loc div is clicked.
What would be the best way to map the result so it doesn't show as loc1 & loc2, desc1 & desc2 but as loc1 & desc1, loc2 & desc2?
Code:
var places = {
library: {
location: [
{
loc_name: "library1",
"desc": "desc1 : Modern and spacious building"
},
{
loc_name: "library2",
"desc": "desc2 : A cosy small building"
}
]
}
};
function contentClass(isShow) {
if (isShow) {
return "content";
}
return "content invisible";
}
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = { isShow: false };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(function (prevState) {
return { isShow: !prevState.isShow };
});
}
render() {
const libraries_desc = places.library.location.map((libr) =>
<div>
<p>{libr.desc}</p>
</div>
);
const lib_names = places.library.location.map((libr) =>
<div>
<p>{libr.loc_name}</p>
</div>
);
return (
<div>
<div className='control' onClick={this.handleClick}>
<h4>{lib_names}</h4>
<div className={contentClass(this.state.isShow)}>{libraries_desc}</div>
</div>
</div>
);
}
}
render((
<Toggle />
), document.getElementById('root'));
Current result:
library1
library2
desc1 : Modern and spacious building
desc 2 : A cosy small building
Desired Result:
library1
desc1 : Modern and spacious building (hidden but shown when clicked)
library2
desc 2 : A cosy small building (hidden but shown when clicked)
Codesandbox
I might try extracting a location into a separate component. By extracting it, each location is responsible for knowing its state. In your case, that means its visibility (controlled by this.state.isShow).
Here's how you could do it:
import React from 'react';
import { render } from 'react-dom';
var places = {
library: {
location: [
{
loc_name: "library1",
"desc": "Modern and spacious building"
},
{
loc_name: "library2",
"desc": "A cosy small building"
}
]
}
};
class Location extends React.Component {
constructor(props) {
super(props);
this.state = { isShow: false };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(function (prevState) {
return { isShow: !prevState.isShow };
});
}
contentClass(isShow) {
if (isShow) {
return "content";
}
return "content invisible";
}
render() {
return (
<div className='control' onClick={this.handleClick}>
<h4>{this.props.desc}</h4>
<div className={this.contentClass(this.state.isShow)}>{this.props.loc_name}</div>
</div>
)
}
}
class Toggle extends React.Component {
constructor(props) {
super(props);
}
render() {
const locations = places.library.location.map(location => {
return <Location {...location} />
})
return (
<div>
{locations}
</div>
);
}
}
render((
<Toggle />
), document.getElementById('root'));
Your Toggle Component should be like this.
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {
isShow: false,
id: -1, // initial value
};
}
handleClick = (id) => {
this.setState({
isShow: !this.state.isShow,
id: id
});
}
render() {
const { location } = places.library;
const { isShow, id } = this.state;
return (
<div className="control">
{location.map((libr, index) => (
<div key={index} onClick={() => { this.handleClick(index) }}>
<p>{libr.loc_name}</p>
{(isShow && (id === index)) && <p>{libr.desc}</p>}
</div>
))}
</div>
);
}
}
So when you click on the div element. A click event will be triggered called handleClick which will pass the index as a param to the function. which will set isShow to false or truth and vice versa along with the current element you want to show which will be selected through this.state.id. So everytime isShow is true and this.state.id matched index element of the array. Your description will show otherwise it will be hidden as you want.
So your desired result will be something like this.
library1
desc1 : Modern and spacious building (hidden but shown when clicked)
library2
desc 2 : A cosy small building (hidden but shown when clicked)