After now two full days of trial and error and googling, I am starting to lose my mind and really could use some help. This is my second React hobby-project, so please bear with me if it contains any "no-go's".
Situation:
I call an API, store the data in state (hotel rooms), display all rooms at first. After applying a search, I want to narrow it down further - and that works (with hardcoded values for testing).
I take all available rooms, filter them, store them into another array and let that array then get displayed.
The Problem:
My search is not dynamic. I can narrow the results down, but I can't "bring them back up". For example: if a user wants the rooms narrowed down by price and by "pets allowed", it gets displayed. But if he decides that bringing his pet is not that important and unchecks the filter, the results stay the same as before.
The handleClicks and Buttons are just there to quickly test things, they're not how the end result will be. Also, I've left out the fetchRoomsData(), as it's not important here.
If anyone could help me out here, it would be highly appreciated!
Thanks in advance :)
import React, { Component } from "react";
import Roomcard from "./RoomCard.js";
export default class AllRooms extends Component {
constructor() {
super();
this.state = {
data: undefined,
fetched: false,
roomsToDisplay: [],
hasFilter: {
price: 300,
capacity: 3,
pets: true,
breakfast: false,
},
};
}
componentDidMount() {
this.fetchRoomsData();
}
handleClick1() {
this.filterByPrice();
}
handleClick2() {
this.filterByPets();
}
handleClick3() {
this.filterByCapacity();
}
handleClick4() {
this.filterByBreakfast();
}
handleClick5() {
this.generateAllRooms();
}
filterByPrice() {
let tempArr = [];
this.state.roomsToDisplay.map((room) =>
room.props.price < this.state.hasFilter.price ? tempArr.push(room) : null
);
if (tempArr.length > 0) {
this.setState({ roomsToDisplay: tempArr });
} else {
this.setState({
roomsToDisplay: <h1>There are no matching results.</h1>,
});
}
}
filterByPets() {
let tempArr = [];
this.state.roomsToDisplay.map((room) =>
room.props.pets ? tempArr.push(room) : null
);
if (tempArr.length > 0) {
this.setState({ roomsToDisplay: tempArr });
} else {
this.setState({
roomsToDisplay: <h1>There are no matching results.</h1>,
});
}
}
filterByBreakfast() {
let tempArr = [];
this.state.roomsToDisplay.map((room) =>
room.props.breakfast ? tempArr.push(room) : null
);
if (tempArr.length > 0) {
this.setState({ roomsToDisplay: tempArr });
} else {
this.setState({
roomsToDisplay: <h1>There are no matching results.</h1>,
});
}
}
filterByCapacity() {
let tempArr = [];
this.state.roomsToDisplay.map((room) =>
room.props.capacity > this.state.hasFilter.capacity
? tempArr.push(room)
: null
);
if (tempArr.length > 0) {
this.setState({ roomsToDisplay: tempArr });
} else {
this.setState({
roomsToDisplay: <h1>There are no matching results.</h1>,
});
}
}
generateAllRooms() {
let finalDiv = [];
this.state.data.items.map((room) =>
finalDiv.push(
<Roomcard
price={room.fields.price}
titleImage={`https:${room.fields.images[0].fields.file.url}`}
allImages={room.fields.images.map((image) => image.fields.file.url)}
name={room.fields.name.toUpperCase()}
slug={room.fields.slug}
capacity={room.fields.capacity}
type={room.fields.type}
size={room.fields.size}
pets={room.fields.pets}
breakfast={room.fields.breakfast}
featured={room.fields.featured}
description={room.fields.description}
extras={room.fields.extras}
key={Math.random() * 1000}
/>
)
);
this.setState({ roomsToDisplay: finalDiv });
}
render() {
return (
<>
<div className="search-field-outer-box">
<button onClick={() => this.handleClick1()}> Filter By Price </button>
<button onClick={() => this.handleClick2()}> Filter By Pets </button>
<button onClick={() => this.handleClick3()}> Filter By capacity </button>
<button onClick={() => this.handleClick4()}> Filter By breakfast </button>
<button onClick={() => this.handleClick5()}> Reset Filter </button>
</div>
{this.state.data ? (
<div className="room-card-container">{this.state.roomsToDisplay}</div>
) : undefined}
</>
);
}
}
đ Welcome to SO
First of all, don't store jsx elements in the state, prefer to store only values and create the jsx at render time.
Now, what I would do is to have the whole dataset in a state variable (and never modify it) and another for the filtered data
this.state = {
data:[],
filteredData:[]
};
// Here at some point when retrieving the data,
// this.setState({data: fetchedData, filteredData: fetchedData});
filterByBreakfast() {
const dataFiltered = // your code to filter
this.setState({
filterdData: dataFiltered,
});
}
resetFilters() {
// Reset the Filtered Data
this.setState({
filterdData: this.state.data,
});
}
render() {
return {
<div>
<div>
<button onClick={this.filterByBreakfast}> Filter By breakfast </button>
<button onClick={this.resetFilters}> Reset Filter </button>
</div>
<div>
{filteredData.length > 0 ? filteredData.map(item => <div>{item}</div>) : <div>No results</div>}
</div>
</div>
}
}
Related
Thanks for any support. I'm learning React and need to solve the problem consisting in that I can't make React to re-render after an item is deleted from a list.
Firstly I would like to say that I have follow the answers I found searching but still no luck.
The scenario is that I'm using React to fetch a list from and API and render it in the same screen with a form for editing and listing the specific information for every item in the list (fields are just name and lastname). The list is displayed with a button for edit which makes the form for edit, and with another button for delete. The list displays the two only fields which are name and lastname which are displayed using ListGroupItem from reacstrap that when onClick uses the form for listing only. I also have the logic for add items.
I'm able to add, update, list with no problems and re-rendering properly. However when deleting I'm just able to delete the item from the API but I have to manually re-render to display the update list
import React, { Component } from "react";
import { Button, Container, Row, Col } from "reactstrap";
import ListBebes from "./components/ListBebes";
import AddBebeForm from "./components/AddBebeForm";
import EditBebeForm from "./components/EditBebeForm";
import { fetchBebes, fetchBebe, addBebe, deleteBebe } from "./api";
import Websocket from "react-websocket";
class App extends Component {
constructor(props) {
super(props);
this.state = {
bebes: [],
bebe: {},
current_bebe_id: 0,
is_creating: true,
is_fetching: true,
is_justRead: true,
has_updated: false,
};
this.socket = React.createRef();
this.focusSocket = this.focusSocket.bind(this);
this.handleItemClick = this.handleItemClick.bind(this);
this.handleEditClick = this.handleEditClick.bind(this);
this.handleDeleteClick = this.handleDeleteClick.bind(this);
this.handleAddBebe = this.handleAddBebe.bind(this);
this.getData = this.getData.bind(this);
this.handleSaveBebe = this.handleSaveBebe.bind(this);
this.handleOnNombresChange = this.handleOnNombresChange.bind(this);
this.handleOnApellidosChange = this.handleOnApellidosChange.bind(this);
}
componentDidMount() {
this.getData();
}
componentDidUpdate(prevProps, prevState) {
if (this.state.has_updated === true) {
this.getData();
this.setState({ has_updated: false });
}
}
focusSocket() {
this.socket.current.focus();
}
async getData() {
let data = await fetchBebes();
this.setState({ bebes: data, is_fetching: false });
}
async handleItemClick(id) {
let selected_bebe = await fetchBebe(id);
this.setState((prevState) => {
return {
is_creating: false,
is_justRead: true,
current_bebe_id: id,
bebe: selected_bebe,
};
});
}
async handleEditClick(id) {
let selected_bebe = await fetchBebe(id);
this.setState((prevState) => {
return {
is_creating: false,
is_justRead: false,
current_bebe_id: id,
bebe: selected_bebe,
};
});
}
async handleDeleteClick(id) {
let antesBebes = [...this.state.bebes];
console.log(antesBebes);
let index = antesBebes.findIndex((i) => i.id === id);
console.log(`the index es ${index} y el id es ${id}`);
await deleteBebe(id);
antesBebes.splice(index, 1);
console.log(antesBebes);
this.setState({ bebes: [...antesBebes], has_updated: true });
//this.setState({ bebes: this.state.bebes, has_updated: true });
//console.log(antesBebes);
console.log("it was deleted...");
//window.location.reload();
//this.setState((prevState) => {
//return {
//bebes: antesBebes,
//has_updated: true,
//};
//});
//this.getData();
}
handleAddBebe() {
this.setState((prevState) => {
return { is_creating: true };
});
}
async handleSaveBebe(data) {
await addBebe(data);
await this.getData();
}
handleData(data) {
let result = JSON.parse(data);
let current_bebe = this.state.bebe;
if (current_bebe.id === result.id) {
this.setState({ bebe: result });
}
}
handleOnNombresChange(e) {
let nombres = e.target.value;
let current_bebe = this.state.bebe;
current_bebe.nombres = nombres;
this.setState({
bebe: current_bebe,
has_updated: true,
});
const socket = this.socket.current;
socket.state.ws.send(JSON.stringify(current_bebe));
}
handleOnApellidosChange(e) {
let apellidos = e.target.value;
let current_bebe = this.state.bebe;
current_bebe.apellidos = apellidos;
this.setState({
bebe: current_bebe,
has_updated: true,
});
//const socket = this.refs.socket;
const socket = this.socket.current;
socket.state.ws.send(JSON.stringify(current_bebe));
}
render() {
return (
<>
<Container>
<Row>
<Col xs="10">
<h2>Hello</h2>
</Col>
<Col>
<Button color="primary" onClick={this.handleAddBebe}>
Create a new note
</Button>
</Col>
</Row>
<Row>
<Col xs="4">
{this.state.is_fetching ? (
"Loading..."
) : (
<ListBebes
bebes={this.state.bebes}
handleItemClick={(id) => this.handleItemClick(id)}
handleEditClick={(id) => this.handleEditClick(id)}
handleDeleteClick={(id) => this.handleDeleteClick(id)}
></ListBebes>
)}
</Col>
<Col xs="8">
{this.state.is_creating ? (
<AddBebeForm handleSave={this.handleSaveBebe} />
) : (
<EditBebeForm
handleNombresChange={this.handleOnNombresChange}
handleApellidosChange={this.handleOnApellidosChange}
bebe={this.state.bebe}
soloLeer={this.state.is_justRead}
/>
)}
<Websocket
ref={this.socket}
url="ws://127.0.0.1:8000/ws/bebes"
onMessage={this.handleData.bind(this)}
/>
</Col>
</Row>
</Container>
</>
);
}
}
export default App;
Can you debug the following lines? and print [...antesBebes] | this.state.bebes and antesBebes after line 3 ?
I have some suspension about these lines, Can't debug them though because you haven't added all your components in here.
1 antesBebes.splice(index, 1);
2 console.log(antesBebes);
3 this.setState({ bebes: [...antesBebes], has_updated: true });
My recommendation is to use one of the following to manage your state in react application:
React Hooks -- recommended for small application like yours link
React Redux -- link
I found the solution. It happened that by placing code in the delete function handleDeleteClick() and also in componentDidUpdate I was messing things up.
The final code for delete is:
async handleDeleteClick(id) {
let antesBebes = [...this.state.bebes];
let index = antesBebes.findIndex((i) => i.id === id);
await deleteBebe(id);
antesBebes.splice(index, 1);
await this.setState({ bebes: antesBebes });
}
This code may have other problems but as far as the original goal was, this solve the problem.
I'm trying to update a nested state. See below. The problem is that upon clicking on a category checkbox, instead of updating the {categories: ....} object in state, it creates a new object in state:
class AppBC extends React.Component {
constructor(props) {
super(props)
this.state = {
products: [],
categories: []
}
this.handleSelectCategory = this.handleSelectCategory.bind(this);
}
componentDidMount() {
this.setState({
products: data_products,
categories: data_categories.map(category => ({
...category,
selected: true
}))
});
}
handleSelectCategory(id) {
this.setState(prevState => ({
...prevState.categories.map(
category => {
if(category.id === id){
return {
...category,
selected: !category.selected,
}
}else{
return category;
} // else
} // category
) // map
}) // prevState function
) // setState
} // handleSelectCategory
render() {
return(
<div className="bc">
<h1>Bare Class Component</h1>
<div className="main-area">
<Products categories={this.state.categories} products={this.state.products} />
<Categories
categories={this.state.categories}
handleSelectCategory={this.handleSelectCategory}
/>
</div>
</div>
);
};
Initial state before clicking (all categories are selected):
After clicking on an a checkbox to select a particular category, it saves a new object to state (correctly reflecting the category selection) instead of updating the already existin categories property:
Change your update to:
handleSelectCategory(id) {
this.setState(prevState => ({
...prevState,
categories: prevstate.categories.map(
category => {
if (category.id === id) {
return {
...category,
selected: !category.selected,
}
} else {
return category;
} // else
} // category
) // map
}) // prevState function
) // setState
}
I prefer this way, it's more easy for reading
handleSelectCategory(id) {
const index = this.state.categories.findIndex(c => c.id === id);
const categories = [...this.state.categories];
categories[index].selected = !categories[index].selected;
this.setState({ categories });
}
If your purpose is to only change selected property on handleSelectCategory function,
Then you could just do it like
run findIndex on array and obtain index for id match from array of objects.
update selected property for that index
Code:
handleSelectCategory(id) {
let targetIndex = this.state.categories.findIndex((i) => i.id === id);
let updatedCategories = [...this.state.categories];
if (targetIndex !== -1) {
// this means there is a match
updatedCategories[targetIndex].selected = !updatedCategories[targetIndex].selected;
this.setState({
categories: updatedCategories,
});
} else {
// avoid any operation here if there is no "id" matched
}
}
I am working on a quiz and now I would like to show different messages to the user depending on what score they have. This code is working but I when the score is 0 no new state is set.
It seems to have something to do with that prevProps.score is only triggered when you have answered something correctly. Is there some other conditional I could use instead maybe?
Below is all code in current state:
class App extends React.Component {
constructor(props) {
super(props);
// Make it somewhat harder for cheaters to inspect the correct answer.
document.getElementById('quiz').setAttribute('data-props', '');
const data = JSON.parse(this.props.quiz);
this.state = {
data: data,
nr: 0,
total: data.length,
showButton: false,
questionAnswered: false,
score: 0,
popUpClass: 'popup-visible',
quizVisible: false,
id: data[0].id,
question: data[0].question,
answers: [
data[0].answers[0],
data[0].answers[1],
data[0].answers[2],
data[0].answers[3]
],
correct: data[0].correct
}
this.nextQuestion = this.nextQuestion.bind(this);
this.handleShowButton = this.handleShowButton.bind(this);
this.handleStartQuiz = this.handleStartQuiz.bind(this);
this.handleIncreaseScore = this.handleIncreaseScore.bind(this);
}
pushData(nr) {
const data = this.state.data;
this.setState({
question: data[nr].question,
answers: [data[nr].answers[0], data[nr].answers[1], data[nr].answers[2], data[nr].answers[3] ],
correct: data[nr].correct,
nr: this.state.nr + 1
});
}
nextQuestion() {
let { nr, total} = this.state;
if(nr === total){
this.setState({
popUpClass: 'popup-visible',
quizVisible: false
});
} else {
this.pushData(nr);
this.setState({
showButton: false,
questionAnswered: false,
quizVisible: true
});
}
}
handleShowButton() {
this.setState({
showButton: true,
questionAnswered: true
});
}
handleStartQuiz() {
this.setState({
popUpClass: 'popup-hidden',
quizVisible: true,
nr: 1
});
}
handleIncreaseScore() {
this.setState({
score: this.state.score + 1
});
}
render() {
let { nr, total, id, question, answers, correct, showButton, questionAnswered, popUpClass, quizVisible, score} = this.state;
return (
<div className="app-container">
<Popup className={popUpClass} score={score} total={total} startQuiz={this.handleStartQuiz} key={nr} />
{quizVisible ?
(
<div key={question} className="quiz">
<div className="quiz-box">
<span className="question-total">FrÄga {nr} av {total}</span>
<h2 className="question">{question}</h2>
<Answers
key={id}
answers={answers}
correct={correct}
showButton={this.handleShowButton}
isAnswered={questionAnswered}
increaseScore={this.handleIncreaseScore} />
</div>
<div id="submit">
{showButton ? <button className="fancy-btn" onClick={this.nextQuestion} id={nr === total ? 'finishQuiz' : null}>{nr === total ? 'Slutför quiz' : 'NÀsta frÄga'}</button> : null}
</div>
</div>
) : null}
</div>
);
}
};
class Popup extends React.Component {
constructor(props) {
super(props);
this.state = {
title: 'Quiz',
showStartButton: true
};
this.startQuizHandle = this.startQuizHandle.bind(this);
}
startQuizHandle() {
this.props.startQuiz();
}
componentDidUpdate(prevProps) {
let total = this.props.total;
let highScore = total - 2;
let halfScore = total / 2;
if (this.props.score !== prevProps.score) {
if (this.props.score >= highScore) {
this.setState({
title: 'You are an expert!',
})
} else if (this.props.score >= halfScore && this.props.score <= highScore) {
this.setState({
title: 'You are pretty good at this!'
})
}
else if (this.props.score < halfScore && this.props.score > 0) {
console.log('score less than half');
this.setState({
title: 'You need some practice.'
})
}
else {
this.setState({
title: 'You did not do too well.',
})
}
}
}
createMarkup(text) {
return {__html: text};
}
render() {
let { title, intro, text, showStartButton } = this.state;
let { className } = this.props;
return (
<div className={`popup-container ${ className }`}>
<div className="popup">
<h1>{title}</h1>
</div>
{showStartButton ? <button className="fancy-btn" onClick={this.startQuizHandle}>Start Quiz</button> : null}
</div>
);
}
}
export default Popup
class Answers extends React.Component {
constructor(props) {
super(props);
this.state = {
isAnswered: false,
classNames: ['', '', ''],
isDisabled: false
}
this.checkAnswer = this.checkAnswer.bind(this);
}
checkAnswer(e) {
let { isAnswered } = this.props;
this.setState({
isDisabled: true
})
if (!isAnswered) {
let elem = e.currentTarget;
let { correct, increaseScore } = this.props;
let answer = Number(elem.dataset.id);
let updatedClassNames = this.state.classNames;
if (answer === correct) {
updatedClassNames[answer - 1] = 'right';
increaseScore();
}
else {
updatedClassNames[answer - 1] = 'wrong';
}
this.setState({
classNames: updatedClassNames
})
this.props.showButton();
}
}
render() {
let { answers } = this.props;
let { classNames } = this.state;
const { isDisabled } = this.state;
return (
<div id="answers">
<ul>
<li onClick={this.checkAnswer} className={classNames[0]} data-id="1"><p className={isDisabled ? "disabled" : null}><span>A</span> {answers[0]}</p></li>
<li onClick={this.checkAnswer} className={classNames[1]} data-id="2"><p className={isDisabled ? "disabled" : null}><span>B</span> {answers[1]}</p></li>
<li onClick={this.checkAnswer} className={classNames[2]} data-id="3"><p className={isDisabled ? "disabled" : null}><span>C</span> {answers[2]}</p></li>
</ul>
</div>
);
}
}
export default Answers
Convert your last else if to an else so your conditional tests are as follows:
if score >= high score => "...expert!"
else if half score <= score < high score => "... petty good"
else if 0 < score < half score => "..need practice"
else => "did not do so well"
This is the "catch-all" branch of logic for scores that didn't fall into one of the previous test cases.
componentDidUpdate
componentDidUpdate(prevProps) {
let total = this.props.total;
let highScore = total - 2;
let halfScore = total / 2;
const { score } = this.props;
if (score !== prevProps.score) {
if (score >= highScore) {
this.setState({
title: "You are an expert!"
});
} else if (score >= halfScore && score <= highScore) {
this.setState({
title: "You are pretty good at this!"
});
} else if (score < halfScore && score > 0) {
this.setState({
title: "You need some practice."
});
} else {
this.setState({
title: "You did not do too well."
});
}
}
}
Edit
Looks like your initial quiz state is to have total be the length of the data array (presumably the quiz questions?) and score starting at 0. From your logic it is clear score monotonically increases from 0. I think the piece that is missing from the Popup component is checking this "initial state" where the score === 0. My guess is you see the title start at "Quiz" and then after the first correct answer it updates to "You need some practice.". A small refactor of the "checking score" logic within componentDidUpdate will allow you to check it when Popup first mounts when the score is 0.
checkScore = () => {
const { score, total } = this.props;
const highScore = total - 2;
const halfScore = total / 2;
if (score >= highScore) {
this.setState({
title: "You are an expert!"
});
} else if (score >= halfScore && score < highScore) {
this.setState({
title: "You are pretty good at this!"
});
} else if (score < halfScore && score > 0) {
this.setState({
title: "You need some practice."
});
} else {
this.setState({
title: "You did not do too well."
});
}
}
componentDidMount() {
this.checkScore();
}
componentDidUpdate(prevProps) {
const { score } = this.props;
if (prevProps.score !== score) {
this.checkScore();
}
}
Updated the above linked codesandbox.
I have this program that brings an article from my data base in componentDidMount(), fragmentedArticle() grabs each word and put it in this.state.fragmented and each word is put it in a span tag in this.state.fragmentedTags
I print the article in grey color text, but I want to change the style color property of the text (with a setTimeout every 1000 milliseconds) but I don't know if it's posible to changed a property of a tag that is save it in the react state.
import React, { Component } from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';
export default class ArticleDetails extends Component {
constructor(props) {
super(props);
this.state = {
id: '',
title: '',
article: '',
date: new Date(),
lenguages: [],
articles: [],
fragmented: [],
fragmentedTags: [],
showSpans: false,
counterSpaces: 0,
}
this.deleteArticle = this.deleteArticle.bind(this);
this.fragmentedArticle = this.fragmentedArticle.bind(this);
this.coloredArticle = this.coloredArticle.bind(this);
}
componentDidMount() {
this.setState({
id: this.props.match.params.id
})
// get individual exercise.
axios.get('http://localhost:5000/articles/'+ this.props.match.params.id)
.then(response => {
this.setState({
title: response.data.title,
article: response.data.article,
duration: response.data.duration,
date: new Date(response.data.date)
})
})
.catch(function (error) {
console.log(error);
})
// get all lenguages.
axios.get('http://localhost:5000/lenguages/')
.then(response => {
if (response.data.length > 0) {
this.setState({
lenguages: response.data.map(lenguage => lenguage.lenguage),
})
}
}).catch( error => console.log(error) )
}
deleteArticle( id ) {
axios.delete( 'http://localhost:5000/articles/' + id )
.then( res => console.log( res.data ) );
this.setState({
articles: this.state.articles.filter( el => el._id !== id )
}
)
}
fragmentedArticle = () => {
let length = this.state.article.length;
let word = [];
let fragmentedArticle = [];
let counter = 0;
let p1, p2 = 0;
for (let x = 0; x <= length; x++) {
word[x] = this.state.article[x];
if( this.state.article[x] === ' ' || this.state.article[x] === "\n" ){
p2 = x;
fragmentedArticle[counter] = word.join('').substr(p1,p2);
p1 = p2
p2 = 0;
counter++;
}
}
// we save each word
this.setState({
fragmented: fragmentedArticle,
counterSpaces: counter,
showSpans: !this.state.showSpans,
})
// we save each word wrapped in a span tag with a property of color grey.
this.setState( prevState => ({
fragmentedTags: prevState.fragmented.map( (name, index) =>
<span key={ index } style={{color:'grey'}} >{name}</span>
)
}))
}
coloredArticle = () => {
console.log(this.state.fragmentedTags[0].props.style.color);
// I see the actual value color style property of the span tag (grey) but I want to change it on green from the this.state.fragmentedTags[0] to the last word within a x period of time with the setTimeout js method.
// this code bellow change the color but not one by one.
this.setState( prevState => ({
fragmentedTags:
// map all the elements
prevState.fragmented.map( (name, index) =>
// with a delay of 1500 milliseconds
setTimeout(() => {
<span key={ index } style={{color:'green'}} >{name}</span>
}, 1500)
)
})
)
}
render(props) {
const displaySpan = this.state.showSpans ? 'inline-block' : 'none';
const {fragmentedTags} = this.state
return (
<div>
<h6>{ this.state.title }</h6>
{/* this show/hide the article text */}
<p onClick={ this.fragmentedArticle }>Show</p>
{/* I want to changed the text color one by one within a period of time (velocity, setTimeout) */}
<p onClick={ this.coloredArticle }>Play</p>
{/* Show us the full article (each word wrapped in a span with its property) */}
<div style={{ display:displaySpan }}>
{ fragmentedTags }
</div>
</div>
)
}
}
You shouldn't be transforming state like that. It gets very difficult to debug your application and makes it much more difficult to do simple things.
Download your articles and save them into state but if you need to make any other changes save it into a new part of state rather than overwriting current state. Most likely you do not need to save transformations into state though.
To answer your question, I would set a timestamp for each article and once its downloaded set a timer that will rerender the article with the new changes if sufficient time has passed.
im new to react, trying to make an todolist website, i have the add and delete and displaying functionality done, just trying to add an search function, but i cant seem to get it working, where as it doesn't filter properly.
i basically want to be able to filter the values on the todos.title with the search value. such as if i enter an value of "ta" it should show the todo item of "take out the trash" or any item that matches with that string.
when i try to search, it gives random outputs of items from the filtered, i am wondering if my filtering is wrong or if i am not like displaying it correctly.
ive tried to pass the value into todo.js and display it there but didn't seem that was a viable way as it it should stay within App.js.
class App extends Component {
state = {
todos: [
{
id: uuid.v4(),
title: "take out the trash",
completed: false
},
{
id: uuid.v4(),
title: "Dinner with wife",
completed: true
},
{
id: uuid.v4(),
title: "Meeting with Boss",
completed: false
}
],
filtered: []
};
// checking complete on the state
markComplete = id => {
this.setState({
todos: this.state.filtered.map(todo => {
if (todo.id === id) {
todo.completed = !todo.completed;
}
return todo;
})
});
};
//delete the item
delTodo = id => {
this.setState({
filtered: [...this.state.filtered.filter(filtered => filtered.id !== id)]
});
};
//Add item to the list
addTodo = title => {
const newTodo = {
id: uuid.v4(),
title,
comepleted: false
};
this.setState({ filtered: [...this.state.filtered, newTodo] });
};
// my attempt to do search filter on the value recieved from the search field (search):
search = (search) => {
let currentTodos = [];
let newList = [];
if (search !== "") {
currentTodos = this.state.todos;
newList = currentTodos.filter( todo => {
const lc = todo.title.toLowerCase();
const filter = search.toLowerCase();
return lc.includes(filter);
});
} else {
newList = this.state.todos;
}
this.setState({
filtered: newList
});
console.log(search);
};
componentDidMount() {
this.setState({
filtered: this.state.todos
});
}
componentWillReceiveProps(nextProps) {
this.setState({
filtered: nextProps.todos
});
}
render() {
return (
<div className="App">
<div className="container">
<Header search={this.search} />
<AddTodo addTodo={this.addTodo} />
<Todos
todos={this.state.filtered}
markComplete={this.markComplete}
delTodo={this.delTodo}
/>
</div>
</div>
);
}
}
export default App;
search value comes from the header where the value is passed through as a props. i've checked that and it works fine.
Todos.js
class Todos extends Component {
state = {
searchResults: null
}
render() {
return (
this.props.todos.map((todo) => {
return <TodoItem key={todo.id} todo = {todo}
markComplete={this.props.markComplete}
delTodo={this.props.delTodo}
/>
})
);
}
}
TodoItem.js is just the component that displays the item.
I not sure if this is enough to understand the issue 100%, i can add more if needed.
Thank you
Not sure what is wrong with your script. Looks to me it works fine when I am trying to reconstruct by using most of your logic. Please check working demo here: https://codesandbox.io/s/q9jy17p47j
Just my guess, it could be there is something wrong with your <TodoItem/> component which makes it not rendered correctly. Maybe you could try to use a primitive element such as <li> instead custom element like <TodoItem/>. The problem could be your logic of markComplete() things ( if it is doing hiding element works ).
Please let me know if I am missing something. Thanks.