I have an array with objects sorted with a date entry and a component to list in what approximated time it was listed.
const TimeHeader = ({date}) => {
const calculateDays = (date) => {
let timeDiff = new Date().getTime() - new Date(date).getTime();
let days = Math.ceil(timeDiff / (1000 * 3600 * 24));
return days;
}
let daysGone = calculateDays(date);
let color = "green";
let text = "Less than 1 Week";
if (daysGone > 7 && daysGone <= 30){
color = "blue";
text = "Less than 1 Month";
} else if (daysGone > 30 && daysGone <= 90){
color = "yellow";
text = "Less than 3 Months";
} else if (daysGone > 90){
color = "red";
text = "More than 3 Months";
}
return (
<div style={{backgroundColor: color}}>{text}</div>
)
}
How would I call upon this component to be rendered only once for each instance? (Once for Less than 1 week, once for less than one month etc)
Right now I am just calling it before each item listing but of course it leads a lot of repetition.
{list.map(item => {
return (
<div key={item.id}>
<TimeHeader date={item.date} />
<ItemDisplay item={item} />
</div>
)
})}
One solution would be to split the array to different categories beforehand, but I'm wondering if there is a nice solution that doesn't require splitting the array.
Related
ReactJS - I implement Binary Search Function, it works only first time but after I change the value in the input box, it always return -1 even it has the value in the Array.
Please see the following code:
import React, { useState } from 'react'
import { Container } from 'react-bootstrap'
const binarysearch = () => {
const [ insertValue, setInsertValue ] = useState(0)
var exarr = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]
// Binary Search
const binarySearch = (arr, val) => {
let start = 0, end = arr.length - 1
while (start <= end) {
let mid = Math.floor((start + end)/2)
console.log(mid)
if (arr[mid] === val) {
return mid
}
if (val < arr[mid]) {
end = mid - 1
} else {
start = mid + 1
}
}
return -1
}
// End Binary Search
return (
<div>
<br />
<hr />
<Container>
<h1>Binary Search</h1>
<h4>Array = {JSON.stringify(exarr)}</h4>
<h4>Search <input type="number" onChange={e => setInsertValue(e.target.value)} /></h4>
<h3>Search {insertValue}, Result in location: {binarySearch(exarr,insertValue)}</h3>
</Container>
<hr />
</div>
)
}
export default binarysearch
First Time Load
After Change Input (Search 10 it should return 10 but -1)
The problem is the fact that e.target.value is always a string, even when the input type attribute is set to "number".
So, when you do arr[mid] === val it will be always false, since this is comparing a number to a string.
You can see this behaviour here.
To fix this, do onChange={e => setInsertValue(Number(e.target.value))}.
Or, alternatively, you can use the non strict equality comparison, which is not really recommended, by replacing the === operator by just ==.
Thank you very much #Mario Vernari
I update the below line to change from string to number, it works properly.
(Insert '+' to insertValue)
From
<h3>Search {insertValue}, Result in location: {binarySearch(exarr,insertValue)}</h3>
To
<h3>Search {insertValue}, Result in location: {binarySearch(exarr, +insertValue)}</h3>
Im trying to do a Pomodoro Clock timer, which is basically two timers that alternate between.
Thats all the code:
import React, { useState } from "react";
function Challenge20() {
const [timer, setTimer] = useState('');
let minutes = 0;
let seconds = 0;
const [workRest, setWorkRest] = useState('work');
function startTimer() {
document.getElementById('start').style.display = 'none';
minutes = document.getElementById('work').value - 1;
seconds = 59;
setInterval(reduceSeconds, 1000);
};
function reduceSeconds() {
if (seconds < 10) {
setTimer(minutes + ':' + '0' + seconds);
}
else {
setTimer(minutes + ':' + seconds);
}
seconds -= 1;
if (seconds < 1 && minutes > 0) {
seconds = 59;
minutes -= 1;
}
else if (seconds == 0 && minutes == 0){
setWorkRest(workRest == 'work' ? 'rest' : 'work');
minutes = document.getElementById(workRest == 'work' ? 'work' : 'rest').value;
}
};
return (
<>
<label>Work Minutes:</label>
<input id='work' type='number' max='60'/>
<br/>
<label>Rest Minutes:</label>
<input id='rest' type='number' max='60'/>
<br/>
<br/>
<span id='timer'>{workRest} -> {timer}</span>
<button id='start' onClick={() => startTimer()}>Start!</button>
</>
);
};
export default Challenge20;
The problem is in this part:
else if (seconds == 0 && minutes == 0){
setWorkRest(workRest == 'work' ? 'rest' : 'work');
minutes = document.getElementById(workRest == 'work' ? 'work' : 'rest').value;
}
The setState is not changing from 'work' to 'rest', also tried to call a function to change the state, clearing interval and 2 separated if, nothing worked, what am I doing wrong?
useState is not work inside the condition. For ex: you are set the state value in the if condition. State value not updated in condition.
I think this is what you're trying to achieve? The problem is that the timer keeps going. On the next iteration, it sets workRest back to its previous value. To solve this, I used clearInterval to stop iterating, and decremented seconds to display 00:00 on the timer. As such, I had to assign the interval creation to a variable we can pass into clearInterval.
import React, { useState } from "react";
function Challenge20() {
const [timer, setTimer] = useState("");
let minutes = 0;
let seconds = 0;
const [workRest, setWorkRest] = useState("work");
let interval;
function startTimer() {
document.getElementById("start").style.display = "none";
minutes = document.getElementById("work").value - 1;
seconds = 59;
interval = setInterval(reduceSeconds, 1);
}
function reduceSeconds() {
if (seconds < 10) {
setTimer(minutes + ":" + "0" + seconds);
} else {
setTimer(minutes + ":" + seconds);
}
seconds -= 1;
if (seconds < 1 && minutes > 0) {
seconds = 59;
minutes -= 1;
} else if (seconds == 0 && minutes == 0) {
console.log();
setWorkRest(workRest == "work" ? "rest" : "work");
minutes = document.getElementById(workRest == "work" ? "work" : "rest")
.value;
clearInterval(interval);
seconds -= 1;
}
}
return (
<>
<label>Work Minutes:</label>
<input id="work" type="number" max="60" />
<br />
<label>Rest Minutes:</label>
<input id="rest" type="number" max="60" />
<br />
<br />
<span id="timer">
{workRest} -> {timer}
</span>
<button id="start" onClick={() => startTimer()}>
Start!
</button>
</>
);
}
Hi I'm fairly new to React and currently trying to write a word counter. The idea is that once you type in the text box it will then display a list of all the words and the frequency of how often they're used (This would be displayed in the span tag where it says wordCounts). The issue I'm currently having is it only displays one word with the frequency when I want a list. Moreover I honestly feel like could be achieved in a completely different way but again I'm fairly new to React and learning as I go.
If I need to share any more info or more code, please let me know.
React Code
import { Component } from "react";
import "./App.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
firstValue: "",
numberOfCharacters: "",
withoutWhiteSpace: "",
numberOfWords: "",
linesCount: "",
wordSelectionCount: "",
};
}
firstHandle = (event) => {
var input = event.target.value;
const text = document.getElementById("textarea").value;
const linesCount = text.split("/\r|\r\n|\n/").length;
const numberOfCharacters = input === "" ? 0 : input.split("").length;
const withoutWhiteSpace =
input === "" ? 0 : input.split("").filter((char) => char !== " ").length;
const words =
input === "" ? 0 : input.split(" ").filter((word) => word.trim()).length;
const lines = input === "" ? 1 : input.split(/\n/g).length;
this.setState({
firstValue: input,
numberOfCharacters: numberOfCharacters,
withoutWhiteSpace: withoutWhiteSpace,
numberOfWords: words,
linesCount: lines,
wordSelectionCount: "",
});
};
// This function is responsible for the word counting
wordCounter = (e) => {
e.preventDefault();
var keys = [];
var counts = {};
const input = this.state.firstValue
.replace(/\W/g, " ")
.replace(/[0-9]/g, " ")
.split(" ")
.filter((word) => word.trim());
for (let i = 0; i < input.length; i++) {
var word = input[i];
if (counts[word] === undefined) {
counts[word] = 1;
keys.push(word);
} else {
counts[word] += 1;
keys.push(word);
// console.log(keys);
}
keys.sort();
for (let i = 0; i < keys.length; i++) {
var key = keys[i];
var result = key + " - " + counts[key];
console.log(result);
}
this.setState({
wordSelectionCount: result,
});
}
};
render() {
var numberOfCharacters = this.state.numberOfCharacters;
var withoutWhiteSpace = this.state.withoutWhiteSpace;
var words = this.state.numberOfWords;
var lines = this.state.linesCount;
var wordCounts = this.state.wordSelectionCount;
console.log(wordCounts);
return (
<div className="App">
<header className="App-header">
<form>
<h1>Character Counter</h1>
<p>
Characters <span>{numberOfCharacters}</span> Without White Space{" "}
<span>{withoutWhiteSpace}</span> Words <span>{words}</span> Lines{" "}
<span>{lines}</span>
</p>
<textarea
id="textarea"
type="text"
placeholder="Please type some text..."
value={this.firstValue}
onChange={this.firstHandle}
/>
<h1>Word Counting</h1>
{/* This button calls the wordCounter Method which should display all the Word listings */}
<button className="btn" onClick={this.wordCounter}>
Words Count
</button>
<p>
<span>{wordCounts}</span>
</p>
</form>
</header>
</div>
);
}
}
export default App;
Issue
The issue is you are not iterating over wordSelectionCount to render your data, therefore your latest value will be displayed.
You can iterate over wordSelectionCount to render all of the data. But, I have a suggestion for you
Suggestion
First suggestion will be, use functional components and react hooks.
Second, use the power of the object's [key-value] pair to store the word counts.
I have created a codesandbox example if you want to have a look. You can start adding words to see the word counter.
Solution for the existing code
instead of rendering <span>{wordCounts}</span> directly, you should iterate over it. Such as:
this.state.wordSelectionCount && Object.keys(this.state.wordSelectionCount).map(word => (
<span>{word} - {this.state.wordSelectionCount[word]}</span>
)}
I have these three imports and I want to use them accordingly.
import avatar1 from "../../../../assets/images/user/avatar-1.jpg";
import avatar2 from "../../../../assets/images/user/avatar-2.jpg";
import avatar3 from "../../../../assets/images/user/avatar-3.jpg";
And I have this map function and I want to show these three images as map function executes.
So I wrote this:
{this.state.empData.map((emp, index) => (
<tr className="unread" key={emp.id}>
<td>
{index + 1 <= 3 ? (
<img
className="rounded-circle"
style={{ width: "40px" }}
src={`avatar` + { index }}
alt="activity-user"
/>
) : (
(index = 0) // i want to reset the index, so it would start from 0 again
)}
</td>
</tr>
))}
So what i want is, if I have 10 iterations through that map function, and I have 3 avatars, I want to show each 10 image so each 3 avatars are repeated from avatar1 to avatar3 as long as map iterates.
Above method I tried, doesn't work as images a re not showing.
Can you help me?
Write a selector function and use the modulus
You can use modulus to repeat a series of numbers. it always returns the remainder of the division. e.g. 3 % 3 === 0 6 % 3 === 0 1 % 3 === 1
const selectImage = (index) {
if (index % 3 === 0) {
return avatar1; // or return "../../../../assets/images/user/avatar-1.jpg"
}
if (index % 3 === 1) {
return avatar2; // or return "../../../../assets/images/user/avatar-2.jpg"
}
if (index % 3 === 2) {
return avatar3; // or return "../../../../assets/images/user/avatar-3.jpg"
}
}
and then in your component
<img
className="rounded-circle"
style={{ width: "40px" }}
src={selectImage(index)}
alt="activity-user"
/>
for (var k = 0; k < 10; k++) {
if (k % 2 === 0) {
weatherText = <div className="in_break">
}
weatherText += <div className="eachD" key={k}>
<div>
{
countIt === 0 ? (currDate.getHours() > 12 ? "Tonight" : "Today") : dayOfWeek[weekDay]
}
</div>
<div>
{
getDate
}
</div>
<div>
{
<ReturnIcon />
}
</div>
</div>
if (k % 2 === 0) {
weatherText += </div>
}
}
What I am looking to do is group all the eachD by two inside the `in_break' div
But I keep getting:
Parsing error: Unexpected token 'weatherText = </div>'
This is the layout:
in_break
eachD
eachD
in_break
eachD
eachD
in_break
eachD
eachD
...
Please help me resolve my issue
UPDATED
I hope this find it's way to your demand:
setWeatherTextItems = (countId, currDate, dayOfWeek, weekDay, getDate) => {
// you make sure all the variables such like countId and currDate are available inside this function.
const items = [];
for (var k = 0; k < 10; k++) {
items.push(
<div className="eachD" key={k}>
<div>
{countIt === 0
? currDate.getHours() > 12
? "Tonight"
: "Today"
: dayOfWeek[weekDay]}
</div>
<div>{getDate}</div>
<div>{<ReturnIcon />}</div>
</div>
);
}
return items;
}
renderInBreak = () => {
const items = this.setWeatherTextItems();
const inBreakItems = [];
let breakBlock = [];
let newBreak = false;
items.forEach((textItem, index) => { //1
if(!newBreak) {
breakBlock.push(textItem);
if(index + 1 === items.length){
inBreakItems.push(breakBlock);
}
} else {
inBreakItems.push(breakBlock);
breakBlock = [];
breakBlock.push(textItem);
//without this condition check, the last element will be left out of an odd array length
if(index + 1 === items.length) {
inBreakItems.push(breakBlock)
}
}
if(index % 2) newBreak = true; //false
else newBreak = false; //false
});
return inBreakItems.map(twoTextWeatherItems => (
<div className="in_break">
{twoTextWeatherItems}
</div>
))
}
render(){
<div>
{this.renderInBreak()}
</div>
}
OLD
React is supposed to handle things differently, maybe this will work:
Define a method in your component that will set your items:
setWeatherTextItems = (countId, currDate, dayOfWeek, weekDay, getDate) => {
// you make sure all the variables such like countId and currDate are available inside this function.
const items = [];
for (var k = 0; k < 10; k++) {
items.push(
<div className="eachD" key={k}>
<div>
{countIt === 0
? currDate.getHours() > 12
? "Tonight"
: "Today"
: dayOfWeek[weekDay]}
</div>
<div>{getDate}</div>
<div>{<ReturnIcon />}</div>
</div>
);
}
return items;
}
in your render method, or where you are willing to render these items:
render(){
<div className="in_break">{this.setWeatherTextItems()}</div>
}
Read more about how to render things in a loop.
You can add the conditions you want inside the for loop, or where it makes sense to you.
Not sure if the logic would work in a react environment but as far as I can see from your plain code when you are going to add the 'in_break' div aren't you just assigning the whole whetherText again instead of joining text to it?
Shouldn't this:
if (k % 2 === 0) {
weatherText = </div>
}
be written like this?
if (k % 2 === 0) {
weatherText += </div>
}
Edit following the typo correction:
I tried to run your code on codepen to have a quicker and easier understanding on how to find a solution.
I created an helper function with your code then I returned
<div className="Container" dangerouslySetInnerHTML={{__html: weatherText}}></div>
This enables you to have the result you are looking for. Only the even elements have the 'in_break' class.
Hope this helped and let me know if this is not correct.
Codepen: https://codepen.io/dpgian/pen/EBzRmX