I am currently working on an app using Firebase (Realtime Database) and ReactJS. I have the following :
I'd like to check in sellersList for values that are true. For each value that is true, i'd like to check into the sellers array for items containing the same value as the key from the first array in the "name : XXX" key-value pair.
Exemple : if i have "Nike" and "Puma" as true, i'd like to check in the sellers array for the items that have "Nike" and "Puma" as a name value, and return the whole parent objects (so i have access to the avatar, the link etc.).
Edit : here is my full component code :
import React, { Component } from 'react';
import Popup from '../Navbar/Popup';
import Upvote from './Upvote';
import actions from '../../actions';
import connectToStores from 'alt-utils/lib/connectToStores';
import ProductStore from '../../stores/ProductStore';
#connectToStores
class ProductPopup extends Component {
constructor(props) {
super(props);
this.state = {
sellers: [],
};
this.commentInput = React.createRef();
this.handleCommentButton = this.handleCommentButton.bind(this);
}
static getStores() {
return [ProductStore];
}
static getPropsFromStores() {
return ProductStore.getState();
}
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.status && this.props.status != nextProps.status) {
actions.getComments(this.props.pid);
}
return true;
}
renderHeader() {
return (
<header>
<section className="product-header-content">
<div className="product-header-thumb">
<img src={this.props.thumbnail} />
</div>
<div className="product-header-info">
<h1>{this.props.name}</h1>
<p>{this.props.tagline}</p>
</div>
</section>
</header>
);
}
renderContent() {
return (
<section className="product-container">
<div className="product-main">
<section className="product-infos_wrapper">
<section>
<img src={this.props.media} className="product-image" />
</section>
<section className="product-infos">
<p>{this.props.description}</p>
</section>
</section>
</div>
<div className="product-aside">
<div className="aside-btn">
<Upvote {...this.props} />
</div>
<div className="product-links-list">
<h3>Ou trouver la paire</h3>
<ul className="product-sellers">
{Object.keys(this.state.sellers)}
</ul>
</div>
</div>
</section>
);
}
handleComment = (e) => {
if (e.keyCode === 13 && e.target.value.length > 0) {
var comment = {
content: e.target.value,
name: this.props.user.name,
avatar: this.props.user.avatar,
};
actions.addComment(this.props.pid, comment);
e.target.value = null;
}
};
handleCommentButton() {
if (this.commentInput.current.value.length > 0) {
var comment = {
content: this.commentInput.current.value,
name: this.props.user.name,
avatar: this.props.user.avatar,
};
}
actions.addComment(this.props.pid, comment);
this.commentInput.current.value = null;
}
renderBodyDiscussion() {
return (
<section className="discussion">
<h2>Commentaires</h2>
{this.props.user ? (
<section className="post-comment">
<img src={this.props.user.avatar} className="medium-avatar" />
<input
placeholder="Que pensez-vous de ce produit ?"
onKeyUp={this.handleComment}
ref={this.commentInput}
/>
<button type="submit" className="btn btn-primary" onClick={this.handleCommentButton}>
Envoyer
</button>
</section>
) : null}
{this.renderComments()}
</section>
);
}
renderBody() {
return (
<section className="product-popup-body">
<main>{this.renderBodyDiscussion()}</main>
</section>
);
}
renderComments() {
return (
<ul className="comment-list">
{this.props.comments.map(function (comment, index) {
return (
<li key={index} className="comment-item">
<div className="comment-item_user">
<img src={comment.avatar} className="medium-avatar" />
<strong>{comment.name}</strong>
</div>
<section className="comment-item_content">
<p>{comment.content}</p>
</section>
</li>
);
})}
</ul>
);
}
render() {
const allSellers = Object.keys(this.props.sellersList).reduce((o, key) => {
this.props.sellersList[key] !== false && (o[key] = this.props.sellersList[key]);
this.state.sellers = o;
return o;
}, {});
console.log(this.props);
return (
<Popup {...this.props} style="product-popup">
{this.renderHeader()}
{this.renderContent()}
{this.renderBody()}
</Popup>
);
}
}
export default ProductPopup;
I think this might be what you're looking to do:
const sellers = [
{ avatar: "1", name: "Nike" },
{ avatar: "2", name: "Puma" },
{ avatar: "3", name: "Something" },
];
const sellersList = {
Nike: true,
Puma: true,
Something: false,
};
const includedSellers = Object.keys(sellersList).filter(
(brand) => sellersList[brand]
);
const results = sellers.reduce(
(results, seller) =>
includedSellers.includes(seller.name) ? [seller, ...results] : results,
[]
);
console.log(results);
//BETTER SOLUTION FROM #Drew Reese
const results2 = sellers.filter(({ name }) => includedSellers.includes(name));
console.log(results2);
This will give you an array of objects from the sellers array, where seller.name is a property from sellersList with a truthy value.
EDIT: As #Drew Reese pointed out, you can achieve this with just filter:
const results = sellers.filter(seller => includedSellers.includes(seller.name));
you have to avoid mutate the state manually like this:
this.state.sellers = o; //
https://reactjs.org/docs/state-and-lifecycle.html#using-state-correctly
Related
I am trying to query my DB for all plants posted by a specific user. My search plants by strain is working perfectly fine, returning a list of the plants with said strain datatype. My findByPostedBy is nearly identical to the findByStrain and I cannot seem to figure out the bug here, as it is simply returning the entire list of plants. Probably a silly mistake because I am a noob.
I can confirm that the username that is being sent to the DB in the get method is correct.
plant.service.js
import http from "../http-common";
class PlantDataService {
getAll() {
return http.get("/plants");
}
get(id) {
return http.get(`/plants/${id}`);
}
create(data) {
return http.post("/plants", data);
}
update(id, data) {
return http.put(`/plants/${id}`, data);
}
delete(id) {
return http.delete(`/plants/${id}`);
}
deleteAll() {
return http.delete(`/plants`);
}
findByStrain(strain) {
return http.get(`/plants?strain=${strain}`);
}
findByPostedBy(postedBy) {
return http.get(`/plants?postedBy=${postedBy}`);
}
}
export default new PlantDataService();
Plants-List.component.js
import React, { Component } from "react";
import PlantDataService from "../services/plant.service";
import { Link } from "react-router-dom";
import PlantView from "../views/PlantView";
import userProfile from "../profile/userProfile";
export default class PlantsList extends Component {
constructor(props) {
super(props);
this.onChangeSearchStrain = this.onChangeSearchStrain.bind(this);
this.retrievePlants = this.retrievePlants.bind(this);
this.refreshList = this.refreshList.bind(this);
this.setActivePlant = this.setActivePlant.bind(this);
this.removeAllPlants = this.removeAllPlants.bind(this);
this.searchStrain = this.searchStrain.bind(this);
this.searchByCurrentUser = this.searchByCurrentUser.bind(this);
this.state = {
plants: [],
userPosts: [],
currentPlant: null,
currentIndex: -1,
searchStrain: "",
currentUser: userProfile.getName()
};
}
componentDidMount() {
this.searchByCurrentUser();
}
onChangeSearchStrain(e) {
const searchStrain = e.target.value;
this.setState({
searchStrain: searchStrain
});
}
postedByCurrentUser(creatorOfPost) {
if(creatorOfPost === this.state.currentUser){
return true;
}
return false;
}
retrievePlants() {
PlantDataService.getAll()
.then(response => {
this.setState({
plants: response.data
});
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
refreshList() {
this.retrievePlants();
this.setState({
currentPlant: null,
currentIndex: -1
});
}
setActivePlant(plant, index) {
this.setState({
currentPlant: plant,
currentIndex: index
});
}
removeAllPlants() {
PlantDataService.deleteAll()
.then(response => {
console.log(response.data);
this.refreshList();
})
.catch(e => {
console.log(e);
});
}
searchStrain() {
PlantDataService.findByStrain(this.state.searchStrain)
.then(response => {
this.setState({
plants: response.data
});
console.log(response.data);
console.log("Plants");
console.log(this.state.plants);
})
.catch(e => {
console.log(e);
});
}
searchByCurrentUser() {
console.log(this.state.currentUser);
PlantDataService.findByPostedBy(this.state.currentUser)
.then(response => {
this.setState({
plants: response.data
});
console.log(response.data);
console.log("Plants");
console.log(this.state.plants);
console.log(this.state.currentUser);
})
.catch(e => {
console.log(e);
});
}
render() {
const { searchStrain, plants, userPosts, currentPlant, currentIndex } = this.state;
return (
<div>
<div className="list row">
<div className="col-md-8">
<div className="input-group mb-3">
<input
type="text"
className="form-control"
placeholder="Search by strain"
value={searchStrain}
onChange={this.onChangeSearchStrain}
/>
<div className="input-group-append">
<button
className="btn btn-outline-secondary"
type="button"
onClick={this.searchStrain}
>
Search
</button>
</div>
</div>
</div>
<div className="col-md-6">
<h4>Plants List</h4>
<ul className="list-group">
{
plants &&
plants.map((plant, index) => (
<li
className={
"list-group-item " +
(index === currentIndex ? "active" : "")
}
onClick={() => this.setActivePlant(plant, index)}
key={index}
>
{plant.strain}
</li>
))}
</ul>
<button
className="m-3 btn btn-sm btn-danger"
onClick={this.removeAllPlants}
>
Remove All
</button>
</div>
<div className="col-md-6">
{currentPlant ? (
<div>
<h4>Plant</h4>
<div>
<label>
<strong>Strain:</strong>
</label>{" "}
{currentPlant.title}
</div>
<div>
<label>
<strong>Date Planted:</strong>
</label>{" "}
{currentPlant.datePlanted}
</div>
<div>
<label>
<strong>Sex:</strong>
</label>{" "}
{currentPlant.sex}
</div>
<div>
<label>
<strong>Class:</strong>
</label>{" "}
{currentPlant.class}
</div>
<div>
<label>
<strong>Posted By:</strong>
</label>{" "}
{currentPlant.postedBy}
</div>
<Link
to={"/plants/" + currentPlant.id}
className="badge badge-warning"
>
Edit
</Link>
</div>
) : (
<div>
<br />
<p>Please click on a Plant...</p>
</div>
)}
</div>
</div>
</div>
);
}
}
This could be a backend issue have you checked if the API is working using an HTTP client like Postman.
Further, I don't think it's a good idea to query the plants created by the user's name as it can cause collisions. Use user id for it.
If you have a table named users with data
id, name, created_at, updated_at
123, xyz, --, , --
Store it like
id, plant, ..., created_by
124, abc, ..., 123
Then you can query using user's id.
The code was working before I made it into a function so I am trying to figure out what is the difference and how to fix it.
import React from 'react';
const Ninjas = ({list}) => {
const ninjaList = list.map( ninjan => {
return (
<div className="ninja" key={ninjaList.id}>
<div>Name : {ninjan.name}</div>
<div>Age: {ninjan.age}</div>
<div>Belt:{ninjan.belt}</div>
</div>
)
})
return(
<div className="ninja-list">
{ninjaList}
</div>
)
}
export default Ninjas
you should try it like this:
import React from 'react';
const Ninjas = ({list}) => {
const ninjaList = list.map( (ninjan , index)=> {
return (
<div className="ninja" key={index}>
<div>Name : {ninjan.name}</div>
<div>Age: {ninjan.age}</div>
<div>Belt:{ninjan.belt}</div>
</div>
)
})
return(
<div className="ninja-list">
{()=>ninjaList()}
</div>
)
}
export default Ninjas
You probably have a typo in this line:
<div className="ninja" key={ninjaList.id}>
You are referencing to the list not to the list element.
This is sample code.
const ninjan = [ { id: 1, name: 'adasdasdasd' },{ id: 2, name: '123' }] ;
const ninjaList = ninjan.map( (ninjan)=> {
return (
<div className="ninja" key={ninjan.id}>
<div>Name : {ninjan.name}</div>
<div>Id : {ninjan.id}</div>
</div>
)
})
return (
<div className="content">
{ Object.keys(ninjaList).length >0 &&
ninjaList
}
</div>
);
today I offer you a new challenge
my problem and the next
in the following code
I map objects in my database
I also map a list of items
and so basically I would like to pass my database and not the Items table
basically I want to do exactly the same thing as in the following code except that instead of using the items array, I would like to be able to use the data array which contains my database
do you have any idea how to do this?
I hope I was precise thanks to you for the help Neff
ps: Sorry for the length of the code i try to do my best to clean a little
import React, { Component } from 'react';
import { CardText, Card,Row, Col, Button } from 'reactstrap';
import axios from 'axios'
import GridLayout from 'react-grid-layout';
import SmsForm from './Sms/SMSForm'
import FruitList from './FruitList'
import './AdminPage.scss'
const UP = -1;
const DOWN = 1;
const entrypoint = process.env.REACT_APP_API_ENTRYPOINT+'/api';
class AdminPage extends Component {
constructor(props) {
super(props);
this.state = {
items: [
{ id: 1, name: "orange", bgColor: "#f9cb9c" },
{ id: 2, name: "lemon", bgColor: "#fee599" },
{ id: 3, name: "strawberry", bgColor: "#e06666" }
],
data: [],
}
handleMove = (id, direction) => {
const { items } = this.state;
const position = items.findIndex(i => i.id === id);
if (position < 0) {
throw new Error("Given item not found.");
} else if (
(direction === UP && position === 0) ||
(direction === DOWN && position === items.length - 1)
) {
return; // canot move outside of array
}
const item = items[position]; // save item for later
const newItems = items.filter(i => i.id !== id); // remove item from array
newItems.splice(position + direction, 0, item);
this.setState({ items: newItems });
};
// rest of the component
onHandleChange(event) {
const name = event.target.getAttribute('name');
this.setState({
message: { ...this.state.message, [name]: event.target.value }
});
}
getRandom = async () => {
const res = await axios.get(
entrypoint + "/alluserpls"
)
this.setState({ data: res.data })
}
componentDidMount() {
this.getRandom()
}
render() {
let datas = this.state.data.map(datass => {
const status = JSON.parse(localStorage.getItem("validated-order") || "{}")[datass.id];
return (
<div>
< Col sm="12" key={datass.id} className="wtfFuHereIsForOnlyBackGroundColorForCol12Nice">
<FruitList fruitList={this.state.items} onMove={this.handleMove} />
<GridLayout className="GridlayoutTextOnlyForGridOuiAndHeigthbecauseHeigthWasBug" layout={layout} cols={12} rowHeight={30} width={1200}>
<div key="a">
<Card body className="yoloCardBodyForbackGroundAndRaduisBorderForAdminPageWtf">
<CardText className="cardTextForAdminPageForDataName"> Commande de {datass.name}</CardText>
</Card>
</div>
</ Col>
</div>
)
})
return (
<div> <div>
<Row className="wtfHereIsAgainOnlyForRowAndMarginForAdminPageJustWtf">
<div className="isJustForOnlyPaddingOnRowForAdminPage" >
<div className="navBarGridMenuAdminPage">
<div className="thatIsOnlyForSpaceArroundOnDivAdminPage">
<CardText className="maybeColForAdminPageOuiOui"> Nouvelles commandes </CardText>
</div>
</div>
<div>
{datas}
</div>
</div>
</Row>
</div>
<div className="box">
</div>
</div>
)
}
}
export default AdminPage
here my second components
import React from "react";
const LEFT = -1;
const RIGHT = 1;
class FruitList extends React.Component {
render() {
const { fruitList, onMove } = this.props;
return (
<div style={{ display: "flex" }}>
{fruitList.map(item => (
<div
key={item.id}
style={{
backgroundColor: item.bgColor,
display: "flex"
}}
>
<div className="fruitsArrows">
<a onClick={() => onMove(item.id, LEFT)}>←</a>
</div>
<div className="fruitsId">{item.id}</div>
<div className="fruitsName">{item.name}</div>
<div className="fruitsArrows">
<a onClick={() => onMove(item.id, RIGHT)}>→</a>
</div>
</div>
))}
</div>
);
}
}
export default FruitList;
To delete particular list do like this-
pass your item(object).
<a onClick={() => onMove(item)}>→</a>
handleMove function
handleMove = (row) => {
let filtered = this.state.items.filter(item=>item.id!==row.id);
this.setState({ items: filtered});
};
I am making a card that lists radio stations and when you click on a station a picture drops down for that list item. all data i am pulling from a json.
I have tried building the list in the toggle.js and in the app.js files
app.js-- div with toggle (its all inside a card..many divs)
<div class="card-trip-infos">
<div>
<Toggle />
</div>
<img class="card-trip-user avatar-bordered"/>
</div>
toggle.js render block:
state = {
on: false,
}
toggle = () => {
this.setState({
on: !this.state.on
})
}
render() {
return (
<ul>
<div>
<p>{PostData.radios[0].name}</p>
{PostData.radios.map((postDetail, index) => {
return <div>
<li onClick={this.toggle}>
<span id='radioName'>{postDetail.name}</span> <span id='frequency'>{postDetail.frequency}</span>
</li>
{
this.state.on && <img src='imagesrc'></img>
}
</div>
})}
</div>
</ul>
)
}
}
I dont know what exactly is wrong but i expect to be able to toggle a picture for each list row seperately. i am confused where to iterate over my json and where to plug everything in.
many Thanks!!
Since we don't know your toggle function and all your component we can't make exact suggestions but in order to do what you want here (just toggle the selected item), you have two different approaches.
You can keep the selected state in the component itself.
class App extends React.Component {
state = {
cards: [
{ id: 1, name: "foo" },
{ id: 2, name: "bar" },
{ id: 3, name: "baz" }
],
};
render() {
const { cards } = this.state;
return (
<div>
{cards.map(card => (
<Card
key={card.id}
card={card}
/>
))}
</div>
);
}
}
class Card extends React.Component {
state = {
selected: false
};
handleSelect = () =>
this.setState(state => ({
selected: !state.selected
}));
render() {
const { card } = this.props;
return (
<div
className={this.state.selected ? "selected" : ""}
onClick={this.handleSelect}
>
{card.id}-{card.name}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
.selected {
background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root" />
You can keep the selected state in the parent component.
class App extends React.Component {
state = {
cards: [
{ id: 1, name: "foo" },
{ id: 2, name: "bar" },
{ id: 3, name: "baz" }
],
selected: {}
};
handleSelect = id =>
this.setState(state => ({
selected: { ...state.selected, [id]: !state.selected[id] }
}));
render() {
const { cards, selected } = this.state;
return (
<div>
{cards.map(card => (
<Card
key={card.id}
card={card}
handleSelect={this.handleSelect}
selected={selected[card.id]}
/>
))}
</div>
);
}
}
const Card = ({ card, handleSelect, selected }) => {
const handleClick = () => handleSelect(card.id);
return (
<div className={selected ? "selected" : ""} onClick={handleClick}>
{card.id}-{card.name}
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
.selected {
background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root" />
Create a component for individual list item, have a state there which will hold a boolean property to tell whether the image is to be shown or not. Render function will look something like this:
render (
return (
<li onClick="this.toggleShowImage()">
<div> {props.data.name} </div>
<div> {props.data.frequency} </div>
{this.state.showImage ? <img src='imageUrl' /> : null }
</li>
)
)
Then in toggle.js iterate through your data and create the list components. The render function will look something like below:
render() {
return (
<ul>
{PostData.radios.map((postDetail, index) => {
return <ListItem key={postDetail.name} data={postDetail} />
})}
</ul>
)
}
The following is the code that I have written while learning react, I have not yet reached the point where state is used. While practicing the use of props, I encountered the error as follows: Error snapshot
I am not able to figure out why the prop is showing as undefined.
I am in the very basic stage of understanding React and would appreciate any help in this.
Thank you
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import moment from 'moment';
import PropTypes from 'prop-types';
let tweet = {
message: "Something about cats.",
gravatar: "xyz",
author: {
handle: "catperson",
name: "IAMA Cat Person"
},
likes: 2,
retweets: 5,
timestamp: "2016-07-30 21:24:37"
};
let from = {
name: "john doe",
addressLine1: "fake house, fake lane",
addressLine2: "fake city, fake state"
}
let to = {
name: "jane doe",
addressLine1: "fake-ish house, fake-ish lane",
addressLine2: "fake-ish city, fake-ish state"
}
function Tweet( {tweet} ) {
return (
<div className="tweet">
<Avatar hash={tweet.gravatar}/>
<div className="content">
<NameWithHandle author={tweet.author}/><Time time={tweet.timestamp}/>
<Message text={tweet.Message}/>
<div className="buttons">
<ReplyButton/>
<RetweetButton count={tweet.retweets}/>
<LikeButton count={tweet.likes}/>
<MoreOptionsButton/>
</div>
</div>
</div>
);
}
function Avatar( {hash} ) {
var url = `https://www.gravatar.com/avatar/${hash}`;
return (
<img src={url} className="avatar" alt="avatar" />
);
}
function Message( {text} ) {
return (
<div className="message">
{text}
</div>
);
}
function NameWithHandle( {author} ) {
const { name, handle } = author
return (
<span className="name-with-handle">
<span className="name">{name} </span>
<span className="handle">#{handle}</span>
</span>
);
}
// The {} is used when there is assignment going on inside the expression the component is pointing to,
// The () is used when there is nothing except a return of JSX from the component.
// If there is use of {} then the return statement is mandatory to return JSX and render.
const Time = ( {time} ) => {
const timeString = moment(time).fromNow();
return <span className="time">{timeString}</span>;
}
const ReplyButton = () => <i className="fa fa-reply reply-button"/>
const RetweetButton = ( {count} ) => (
<span className="retweet-button-span">
<span className="retweet-button-icon"><i className="fa fa-retweet retweet-button"/></span>
{getRetweetCount(count)}
</span>
);
const LikeButton = ( {count} ) => (
<span className="like-button-span">
<span className="like-button-icon"><i className="fa fa-heart like-button"/></span>
{count > 0 &&
<span className="like-count">
{count}
</span>}
</span>
);
const MoreOptionsButton = () => <i className="fa fa-ellipsis-h more-options-button"/>
function getRetweetCount(count) {
if(count > 0) {
return (
<span className="retweet-count">
{count}
</span>
);
} else {
return null;
}
}
const AddressLabel = ( {person} ) => {
const {name, addressLine1, addressLine2} = person;
return(
<div className = "addressLabel">
<div className = "person-name"> {name} </div>
<div className = "person-address1"> {addressLine1} </div>
<div className = "person-address2"> {addressLine2} </div>
</div>
);
}
const Envelope = ( {from}, {to} ) => (
<div>
<AddressLabel person={from}> From: </AddressLabel>
<AddressLabel person={to}> To: </AddressLabel>
</div>
);
LikeButton.propTypes = {
count: PropTypes.number
};
RetweetButton.propTypes = {
count: PropTypes.number
};
Time.propTypes = {
time: PropTypes.string
};
Message.propTypes = {
text: PropTypes.string
};
NameWithHandle.propTypes = {
author: PropTypes.shape({
name: PropTypes.string.isRequired,
handle: PropTypes.string.isRequired
}).isRequired
};
Avatar.propTypes = {
hash: PropTypes.string.isRequired
};
ReactDOM.render(
<div>
<Tweet tweet = {tweet}/>
<Envelope from = {from} to = {to}/>
</div>,
document.querySelector('#root')
);
You have to destructure from and to from the same props object in Envelope:
const Envelope = ( {from, to} ) => (
<div>
<AddressLabel person={from}> From: </AddressLabel>
<AddressLabel person={to}> To: </AddressLabel>
</div>
);