this is my first project using React.js, I want to filter the restaurants cards using checkbox when it it check it show only the restaurants cards with these filters or types true such as the music and WIFI. The problems are it show the default cards perfectly but after I checked the checkbox it's change the all type or filters values into false such as Music and WIFI instead of create or map only the cards that false. In addition, it will not create the default cards after double check, can you please help me
The code:
import React, { Component } from 'react';
import axios from 'axios';
import App from "../App";
import Cards from "../Card";
function CreateCards(resturants) {
//Handel the Music, Wifi, Partition (to transfer it from bolean form into string)
if (resturants.Music == true){
resturants.Music = "Music";
}else{
resturants.Music = "No Music";
}
if (resturants.Wifi == true){
resturants.Wifi = "Wifi";
}else{
resturants.Wifi = "No Wifi";
}
if (resturants.Partition == true){
resturants.Partition = "Partition";
}else{
resturants.Partition = "No Partition";
}
return(
<Cards
key={resturants._id} // done
theCardId={resturants._id} // done
placeName={resturants.Name} // done
stars={resturants.Rating} // done
PRating={resturants.PRating} //until filters
music= {resturants.Music} // done
img={resturants.icon} // need uploads file
status={Status(resturants.OpenTime, resturants.CloseTime)} // done
descreption={resturants.Description} // done
wifi={resturants.Wifi} // done
partition={resturants.Partition} // done
/>
);
}
// Check if the place is open or closed depending on the work hours
function Status (Open, Close){
const date = new Date();
var hours = date.getHours();
const red = 'red';
const green = 'green';
if ((Open <= hours) && (hours < Close)){
// console.log("Open");
return "Open";
}else{
// console.log("Close");
return "Close";
}
}
export default class Resturants extends Component {
//constructor elemnts in login
constructor(props){
super(props);
//intialy no data enterd
this.state = {
resturants: [],
filter: ""
}
this.Filtering = this.Filtering.bind(this);
}
componentDidMount(){
//Get Resturants data
axios.get('http://localhost:3000/places')
.then(resp => {
console.log(resp)
this.setState({
resturants: resp.data
})
})
}
Filtering(e){
// this.setState({filter:e.target.value});
e.preventDefault();
this.state.resturants.filter(Type => {
// console.log(Type.Music === true);
})
}
render(){
return(
<div className="flexthem">
<div className="Filters">
<h4>Filters</h4>
<input className="Checkbox" type="checkbox" id="Type1" value="" onClick={this.Filtering}></input>
</div>
<div className="general-card">
{this.state.resturants.map(CreateCards)}
</div>
</div>
);
}
}
a bit of advice.
use "==="
use function componented.
the hook "useState" is a lot simpler than class component state.
restraunt.music = "music"
is a string.
and
restaurant.music = true
is a boolean.
if you set a variable as a string and try to check if it is false or true after. It will return undefined. If it is an empty string, it will return false.
if (resturants.Music == true){
resturants.Music = "Music";
}else{
resturants.Music = "No Music";
}
in react when you set or change the state, then it refreshes. If you are changing the state with this, you are going to put it through he if statement again. It will return undefined and then not change the checkboxes from their default value.
Related
I am trying to make a simple password strength meter. The issue I am having here is that my code works, but not how I want it to. It seems to be updating the strength message one keyboard press too late. I am not sure how to fix this.
import React, {useState} from "react";
const PasswordStrength = () => {
function passwordStrength(pw) {
return ( /* at least 8 characters */
/.{12,}/.test(pw) /* bonus if longer */
+ /[a-z]/.test(pw) /* a lower letter */
+ /[A-Z]/.test(pw) /* a upper letter */
+ /\d/.test(pw) /* a digit */
+ /[^A-Za-z0-9]/.test(pw) /* a special character */
)
}
// Meter is updating the message with the stroke after
const [pwInput,setpwInput] = useState("")
const [strength, setStrength] = useState(0)
const [hiddenStatus, setHiddenStatus] = useState(true)
const [strengthMessage, setStrengthMessage] = useState("We need a strong password")
const updateScore = (props) => {
//run it through the passwordStrength function
setStrength(passwordStrength(props))
setpwInput(props)
if(strength===0){
setHiddenStatus(true)
setStrengthMessage("We need a strong password")
}
if(strength > 0 && strength <= 2){
//weak sauce
setStrengthMessage("Weak sauce :(")
}
if(strength===3){
//better
setStrengthMessage("Better...")
}
if(strength===4){
//great
setStrengthMessage("Great")
}
if(strength>=5){
//outstanding
setStrengthMessage("Outstanding!")
}
}
return (
<div>Create a password:
<input
type="password"
id="pwd"
placeholder="create a password"
value={pwInput}
onChange= {e => updateScore(e.target.value)}
/>
<progress id="strength" value={strength} max="5" />
<div id="message" aria-hidden={hiddenStatus}>
{strengthMessage}
</div>
</div>
);
}
export default PasswordStrength
before setting strength try storing the password strength value in a variable and then use it instead of the hook inside function for setting strength message
const updateScore = (props) => {
let temp = passwordStrength(props)
setStrength(temp)
setpwInput(props)
if(temp ===0){
setHiddenStatus(true)
setStrengthMessage("We need a strong password")
}
if(temp > 0 && temp <= 2){
//weak sauce
setStrengthMessage("Weak sauce :(")
}
if(temp ===3){
//better
setStrengthMessage("Better...")
}
if(temp ===4){
//great
setStrengthMessage("Great")
}
if(temp >=5){
//outstanding
setStrengthMessage("Outstanding!")
}
}
The issue lies in the fact that setting state is an asynchronous process.
In your updateScore method, you are setting the newly computed strength to the state. But remember that setting a state is asynchronous activity. So you would not get the updated state to compute your strength message, since it hasn't been updated yet.
For this to work properly, instead of using the strength from the state to compute your message, you can use the return value of passwordStrength method.
const updateScore = (props) => {
const newStrength = passwordStrength(props);
setStrength(newStrength);
setpwInput(props);
if(newStrength ===0){
setHiddenStatus(true);
setStrengthMessage("We need a strong password");
}
if(newStrength > 0 && newStrength <= 2){
//weak sauce
setStrengthMessage("Weak sauce :(");
}
if(newStrength ===3){
//better
setStrengthMessage("Better...");
}
if(newStrength ===4){
//great
setStrengthMessage("Great");
}
if(newStrength >=5){
//outstanding
setStrengthMessage("Outstanding!");
}
}
there is a food function with props that is type which is equal to 'Lunch' or 'Dinner'
I need to change the value of LunchStatus when submit is clicked according to the condition on type
const Food = (props) =>{
const [LunchStatus, LunchUpdate] = useState('Lunch YES');
const [DinnerStatus, DinnerUpdate] = useState('Dinner YES');
function handlingSubmit(e){
if(props.type === 'Lunch'){
LunchUpdate('Lunch NO');
console.log(LunchStatus);
}
else{
DinnerUpdate('Dinner NO');
console.log(DinnerStatus);
}
}
return (
<div className='food-box'>
<button class="button_raise" onClick={handlingSubmit}>Submit</button>
</div>
);
}
and output is showing Lunch YES and Dinner YES for first clicks and Lunch NO and Dinner NO for remaining clicks
the output is like when I click both one after one continuously is
Lunch YES
Dinner YES
Lunch NO
Dinner NO
Lunch NO
Dinner NO
There are a couple of reasons for this behavior:
: State is updated asynchronously.
: In any particular render, state and props don't change, changes are only reflected when the component re-renders.
const Food = (props) => {
const [LunchStatus, LunchUpdate] = useState('Lunch YES');
const [DinnerStatus, DinnerUpdate] = useState('Dinner YES');
useEffect(() => {
console.log(LunchStatus);
}, [LunchStatus])
useEffect(() => {
console.log(DinnerStatus);
}, [DinnerStatus])
function handlingSubmit(e) {
if (props.type === 'Lunch') {
LunchUpdate('Lunch NO');
}
else {
DinnerUpdate('Dinner NO');
}
}
return (
<div className='food-box'>
<button class="button_raise" onClick={handlingSubmit}>Submit</button>
</div>
);
}
Your status was updated.
Just you are not seeing because the state is applied after that.
<div className='food-box'>
<p>Lanch state: {{LunchStatus}}</>
<p>Lanch state: {{DinnerStatus}}</>
<button class="button_raise" onClick={handlingSubmit}>Submit</button>
</div>
or
const Food = (props) =>{
const [LunchStatus, LunchUpdate] = useState('Lunch YES');
const [DinnerStatus, DinnerUpdate] = useState('Dinner YES');
function handlingSubmit(e){
if(props.type === 'Lunch'){
LunchUpdate('Lunch NO');
}
else{
DinnerUpdate('Dinner NO');
}
}
console.log(LunchStatus);
console.log(DinnerStatus);
return (
<div className='food-box'>
<button class="button_raise" onClick={handlingSubmit}>Submit</button>
</div>
);
}
Because you are not updating YES at any click, you are only updating NO . Only time it is printing YES is when its initialized with useState('Dinner YES')
I have custom validator like this:
export class PasswordValidator {
static MatchPassword(AC: AbstractControl) {
const formGroup = AC.parent;
if(formGroup) {
let password = formGroup.value.password // to get value in input tag
let confirmPassword = formGroup.value.confirmPassword; // to get value in input tag
if(password != confirmPassword) {
formGroup.get('confirmPassword').setErrors({ matchPassword: true });
} else {
formGroup.get('confirmPassword').setErrors(null);
}
console.log(formGroup.get('confirmPassword').errors);
} else {
return null
}
}
}
And i have added to the form:
this.registerationForm.addControl("confirmPassword", new FormControl('', Validators.compose([Validators.required, PasswordValidator.MatchPassword])));
And in View:
<ion-item class="error-message" *ngIf="registerationForm.controls.confirmPassword.hasError('matchPassword')
&& registerationForm.controls.confirmPassword.touched">
<p>Some message*</p>
</ion-item>
But the problem is i can see the console window but i don't see it reflects in view. The ngIf condition isn't showing the error message
Use detectChanges() when you've updated the model after angular has run it's change detection, or if the update hasn't been in angular world at all.
Use markForCheck() if you're using OnPush and you're bypassing the ChangeDetectionStrategy by mutating some data or you've updated the model inside a setTimeout;
export class PasswordValidator {
static MatchPassword(AC: AbstractControl) {
const formGroup = AC.parent;
if(formGroup) {
let password = formGroup.value.password // to get value in input tag
let confirmPassword = formGroup.value.confirmPassword; // to get value in input tag
if(password != confirmPassword) {
formGroup.get('confirmPassword').setErrors({ matchPassword: true });
} else {
formGroup.get('confirmPassword').setErrors(null);
}
console.log(formGroup.get('confirmPassword').errors);
this.ref.markForCheck();
} else {
return null
}
}
}
add this.ref.markForCheck(); after you update the form.
I have a simple countdown component where a user inputs two times and it counts down the seconds between them. The start, stop, and reset work. Except, when I reset the countdown and input two new times (without refreshing the page), I am hit with this error:
TypeError: _this3.start is not a function
> 108 | <button onClick={(e) => this.start()}>Start</button>
Below is my code:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(){
super();
this.start = this.start.bind(this);
this.toTimestamp = this.toTimestamp.bind(this);
this.getDifference = this.getDifference.bind(this);
this.state = {
input1: '',
input2: '',
countdown: null
}
}
input1ContentChange(e){
const text = e.target.value;
this.setState(()=>{
return {input1: text};
})
}
input2ContentChange(e){
const text = e.target.value;
this.setState(()=>{
return {input2: text};
})
}
toTimestamp(input){
let time = input.split(':');
let seconds = ((+time[0]) * 60 * 60) + ((+time[1]) * 60) + (+time[2]);
return seconds;
}
getDifference(input1, input2){
let difference = (this.toTimestamp(input2))- (this.toTimestamp(input1));
if(this.toTimestamp(input2) < this.toTimestamp(input1)){
alert("please input a later time in Time 2");
}
this.setState({
countdown: difference
})
}
start() {
if(this.state.input1 === '' && this.state.input2 === ''){
alert('please choose 2 times');
}
this.getDifference(this.state.input1, this.state.input2);
this.start = setInterval((e) => {
this.setState((prevState) => {
return {countdown: prevState.countdown - 1};
});
if(this.state.countdown <= 0){
clearInterval(this.start);
}
}, 1000);
}
stop(){
clearInterval(this.start);
}
reset(){
clearInterval(this.start);
this.setState((prevState) => {
return {countdown: null, input1: '', input2:''}
})
}
render() {
return (
<div className="App">
<h1>Countdown Timer</h1>
<p>Please choose two different times below</p>
<div className="input1">
<label>
Time 1:
<input type="time"
step="1"
min= "12:00"
max= "18:00"
value={this.state.input1}
onChange={(e)=> this.input1ContentChange(e)}/>
</label>
</div>
<div className="input2">
<label>
Time 2:
<input type="time"
step="1"
min="12:00"
max="18:00"
value={this.state.input2}
onChange={(e)=> this.input2ContentChange(e)}/>
</label>
</div>
<button onClick={(e) => this.start()}>Start</button>
<button onClick={(e) => this.stop()}>Stop</button>
<button onClick={(e) => this.reset()}>Reset</button>
<h3>{this.state.countdown}</h3>
</div>
);
}
}
export default App;
The error is happening at the start function for restarting the countdown. When I check in my chrome tools using the React extension, the state is managed fine. It seems "this" is getting lost.
You're modifying your class function. When your app loads, your class has a start method, but inside that method you do:
this.start = setInterval(...)
setInterval does not return a function, but an id that you can use later to clear the interval. Even if it did return a function, you probably don't want to be modifying your class methods at runtime.
I would suggest using another variable name:
this.intervalId = setInterval(...)
I have a react-bootstrap React Class, where the createList function gets called on every key input to either of the form inputs (workDone, or hoursAndMinutes). I'm new to reactjs, and perhaps this is normal behavior, but it seems to me that it isn't, and hence I'm doing something wrong.
var SubjectBox = React.createClass({
getInitialState(){
return({
totalHoursAndMinute:0,
subject:'',
workDone:'',
hoursAndMinutes:'',
})
},
dropDownSelected:function(e){
this.setState({subject:e.target.value})
},
handleChangeToWorkDone(){
let s = this.refs.workDone.getValue();
console.log(s);
this.setState({
workDone: s
});
},
validateWorkDone:function(){
let length = this.state.workDone.length;
if (length >= 10) return 'success';
else if (length > 5) return 'warning';
else if (length > 0) return 'error';
},
validateHoursAndMinutes(){
let hm = this.state.hoursAndMinutes.split(':');
if (hm.length === 2){
return 'success';
}else{
return 'error';
}
},
handleChangeToHoursMinute(){
var minutes =0;
let s =this.refs.hoursAndMinutes.getValue();
let hm = s.split(':');
if (hm.length===2){
var h = parseInt(hm[0].trim());
var m = parseInt(hm[1].trim());
if (!m.isNaN){
var minutes = h*60+m;
}
}
this.setState({
hoursAndMinutes: s,
totalMinutes:minutes
});
},
createList: function(){
console.log("create list function.");
var list=[];
for (var i = 0; i < this.props.subjects.length;i++){
list.push(<option key={i} value={this.props.subjects[i].subject}>{this.props.subjects[i].subject}</option>)
}
return list;
},
handleSubmit: function(e){
e.preventDefault();
console.log(this.state.workDone);
console.log(this.state.subject);
},
render(){
return(
<form onSubmit={this.handleSubmit}>
<Input ref="subjectList" type="select" label="Subject" onChange={this.dropDownSelected}>
{this.createList()}
</Input>
<Input ref="workDone"
type="text"
value={this.state.workDone}
placeholder="What did you do?"
label="What did you do" help="Input is 10 or more characters."
bsStyle={this.validateWorkDone()} hasFeedback
groupClassName="group-class" labelClassName="label-class"
onChange={this.handleChangeToWorkDone} />
<Input ref="hoursAndMinutes"
type="text" value={this.state.hoursAndMinutes} placeholder="HH:MM?"
label="How long did you do it?" help="Input in hours:minutes, example 1:5 = an hour and five minutes."
bsStyle={this.validateHoursAndMinutes()} hasFeedback
groupClassName="group-class"
labelClassName="label-class" onChange={this.handleChangeToHoursMinute} />
<Button type="submit" bsStyle="success">Submit</Button>
</form>
)
}
});
It happens because you are using in handleChangeToWorkDone and handleChangeToWorkDone setState which calls re-render
setState() will always trigger a re-render unless conditional
rendering logic is implemented in shouldComponentUpdate(). If mutable
objects are being used and the logic cannot be implemented in
shouldComponentUpdate(), calling setState() only when the new state
differs from the previous state will avoid unnecessary re-renders.
In React Js, the HTML/DOM is always just a representation on the State of your React Component.
Whenever there is a onChange or onBlur or any event, if the State of the React Component is changed(using setState func), the ReactJs component is re-rendered(using Render func).
Only when the State of the React Js component changes, your UI can be updated with the value you key in. Hence, It is the expected behaviour in React Js.