Visualize the URL not the image (data in JSON server) - arrays

I have an array with objects in my JSON server, and when i use .map method to get all the properties of the objects its working, except for the URL of the img. I see in the web app the URL (like a string), but not the image. Why it´s this happening? (Working on React).
This is the code with the mapping:
useEffect(()=>{
getVinos();
},[])
const [vinos,setVinos]= useState([]);
const getVinos = async()=>{
try {
const res = axios.get("http://localhost:3006/vinos").then(res=>{
setVinos(res.data);
})
} catch (error) {
alert('error')
}
}
return (
<div
style={{
color: '#fff',
display: 'flex',
flexDirection: 'column',
paddingLeft: '47px',
alignItems: 'center',
backgroundColor: '#161314',
paddingTop: '30px',
}}>
<h2>Vinos más vendidos del mes</h2>
<h4>Top Selling Wines this Month</h4>
<div>
<StyledWinesContainer>
{vinos.map((vino)=><StyledCards key={vino.id}>{vino.img} <ImgText>Prod.:{vino.nombre}
</ImgText><ImgText>${vino.precio} </ImgText> <Contador/></StyledCards>)}
</StyledWinesContainer>
</div>
</div>
);
And here is the objects array in the JSON server:
{
"vinos": [
{
"id": 1,
"nombre": "Las Perdices, reserva Malbec.",
"precio": 1500.00,
"img": "https://i.ibb.co/xzzVRDG/las-perdices-reserva-malbec.png"
},
{
"id": 2,
"nombre": "Portillo, Cabernet Sauvignon.",
"precio": 2300.00,
"img": "http://i.ibb.co/0f63kY6/portillo-cabernet-sauvignon.png"
},
{
"id": 3,
"nombre": "Alma Mora.",
"precio": 1999.99,
"img": "http://i.ibb.co/tDQP2SN/alma-mora.png"
}
]
}

Related

Can not render array within FlatList

I have read all similar questions in stack overflow on this topic. Yet my info is not being displayed.
I am fetching locations from tomtom api. I have limit the result up to one location for simplicity. My data:
items: Array [
Object {
"address": Object {
"country": "United States",
"countryCode": "US",
"countryCodeISO3": "USA",
"countrySecondarySubdivision": "Hidalgo",
"countrySubdivision": "TX",
"countrySubdivisionName": "Texas",
"extendedPostalCode": "78542-7214",
"freeformAddress": "1718 South 28th Avenue, Edinburg, TX 78542",
"localName": "Edinburg",
"municipality": "Edinburg",
"postalCode": "78542",
"streetName": "South 28th Avenue",
"streetNumber": "1718",
},
"dist": 7911851.058335642,
"entryPoints": Array [
Object {
"position": Object {
"lat": 26.28239,
"lon": -98.14742,
},
"type": "main",
},
],
"id": "840489007442969",
"info": "search:ta:840489007442969-US",
"poi": Object {
"categories": Array [
"company",
"equipment rental",
],
"categorySet": Array [
Object {
"id": 9352038,
},
],
"classifications": Array [
Object {
"code": "COMPANY",
"names": Array [
Object {
"name": "equipment rental",
"nameLocale": "en-US",
},
Object {
"name": "company",
"nameLocale": "en-US",
},
],
},
],
"name": "Wylie Implement Edinbu",
"phone": "+1 956-550-8822",
},
"position": Object {
"lat": 26.28223,
"lon": -98.1464,
},
"score": 1.9846990108,
"type": "POI",
"viewport": Object {
"btmRightPoint": Object {
"lat": 26.2813,
"lon": -98.14536,
},
"topLeftPoint": Object {
"lat": 26.28316,
"lon": -98.14744,
},
},
},
]
My component:
const AutocompleteResults = (props) => {
const [locations, setLocations] = useState(props.items);
console.log("items: ", locations);
useEffect(() => {
setLocations(props.items);
}, [props.items]);
return (
<View style={{ flex: 1, marginBottom: 20 }}>
<Text>Result</Text>
{locations.length>0 ? (
<>
<Text>Items</Text>
<FlatList
style={{ flex: 1, borderColor: "red", borderWidth: 1 }}
horizontal={false}
data={locations}
keyExtractor={(item, index) => index.toString()}
renderItem={({location}) => {
console.log("single location ", location);
return <Text>Location</Text>;
}}
/>
</>
) : null}
</View>
);
};
const style = StyleSheet.create({
viewStyle: {
flex: 1,
justifyContent: "center",
},
});
export default AutocompleteResults;
What I see on the console is: single location undefined
I tried all the suggestions which I found in stack overflow. In my opinion the problem is in extractKey method but I dont know how to fix it.
On the screen I just see the words "Result" "Items" followed by red dash (coming from my styling for flatlist)
EDIT:
I edited my render function in the following way:
renderItem={({ item }) => {
console.log("single location ", item);
return (
<View style={{ flex: 1, height: 30 }}>
<Text>Location</Text>
</View>
);
}}
But the "Location" text still does not display
You are destructuring location in the renderItem function of the FlatList. This fails, because there is no such parameter. The parameter is called item.
This is explained in the documentation.
renderItem({ item, index, separators });
item (Object): The item from data being rendered.
The following code should fix the issue.
return (
<View style={{ flex: 1, marginBottom: 20 }}>
<Text>Result</Text>
{locations.length>0 ? (
<>
<Text>Items</Text>
<FlatList
style={{ flex: 1, borderColor: "red", borderWidth: 1 }}
horizontal={false}
data={locations}
keyExtractor={(item, index) => index.toString()}
renderItem={({item}) => {
console.log("single location ", item);
return <Text>Location</Text>;
}}
/>
</>
) : null}
</View>
);
};
The extractKey function is fine.

Animation using React-spring useTransition not working as expected inside a recursive loop

I've been unable to apply a transition to divs in a comment section i'm working on. The comment section is recursively rendered and there's an instance of useTransition in each one. When the reply button to any of the comments is clicked, the reply form should transition in. Same thing when the show/hide comments is clicked, all its children should transition out however the transition effects are applied to all the divs. i've made a codepen to illustrate the problem. please point me in the right direction. Thank you.
import React, { useState, useEffect } from "https://cdn.skypack.dev/react#17.0.1";
import * as ReactDOM from "https://cdn.skypack.dev/react-dom#17.0.1";
import styled from "https://cdn.skypack.dev/styled-components#5.3.0";
import { useSpring, useTransition, animated } from "https://cdn.skypack.dev/react-spring#9.2.4";
///////// Styled Components ///////////////////////////////
const CommentDisplay = styled(animated.div)`
max-height: 100%;
margin: 10px 0px 0px 25px;
position: relative;
img {
width: 25px;
height: 25px;
margin: 1px 10px 0px 0px;
border-radius: 50%;
border: 1px solid gray;
}
`;
const TopBarWrapper = styled.div`
display: flex;
position: relative;
z-index: -1;
`;
const BorderDiv = styled.div`
position: absolute;
border-left: 1px solid gray;
height: calc(100% - 25px);
width: 100%;
margin-left: 12px;
bottom: 0px;
pointer-events: none;
`;
const CommentBody = styled.p`
overflow-wrap: break-word;
word-wrap: break-word;
-ms-word-break: break-all;
word-break: break-word;
padding-left: 35px;
`;
const BottomBarWrapper = styled.div`
grid-area: bottomBar;
display: flex;
flex-direction: row;
padding-left: 35px;
`;
const Reply = styled.div`
color: rgba(7, 7, 7, 0.65);
cursor: pointer;
padding: 8px 8px 8px 0px;
font-size: 14px;
&:hover{
color: black;
}
`;
const VoteUp = styled.div`
cursor: pointer;
padding: 8px;
&:hover{
background-color: #e5f4fb;
}
svg{
width: 16px;
height: 15px;
margin-right: 4px;
}
span{
font-size: 13px;
}
`;
const VoteDown = styled.div`
cursor: pointer;
padding: 8px;
&:hover{
background-color: #e5f4fb;
}
svg{
width: 16px;
height: 15px;
margin-right: 4px;
}
span{
font-size: 13px;
}
`;
const Form = styled.form`
display: grid;
//grid-template-columns: 90%;
grid-gap: 1.5rem;
grid-area: main_comment_body;
`;
const FormWrapper = styled.div`
display: ${props => props.rows[props.commentid] == "true" ? "grid" : "grid"};
grid-template-columns: minmax(min-content, max-content) 1fr;
grid-template-rows: minmax(50px, 1fr) minmax(min-content, max-content);
grid-template-areas:
"main_comment_img main_comment_body "
"main_comment_img main_comment_buttons";
margin: 0px 50px 0px 85px;
z-index: ${props => props.rows[props.commentid] == "true" ? "1" : "-1"};
opacity: ${props => props.rows[props.commentid] == "true" ? "1" : "0"};
height: ${props => props.rows[props.commentid] == "true" ? "initial" : "0px"};
min-height: ${props => props.rows[props.commentid] == "true" ? "100px" : "0px"};
position: relative;
top: ${props => props.rows[props.commentid] == "true" ? "7px" : "-100px"};
left: 0;
background-color: F4F4F4;
transition: all .05s ease 0s;
img {
width: 25px;
height: 25px;
grid-area: avatar;
margin: 1px 10px 0px 0px;
border-radius: 50%;
border: 1px solid gray;
grid-Area: main_comment_img;
}
`;
const CommentSection = () => {
//in the useEffect hook, an ajax call gets all the comments from the rails server (in this case, it's a hard coded //"allComments" object below) and recursivley gets the id of every comment, reply, reply of reply etc.. and builds an //object in the format of {"comment id" : " false"}.
//
// example showMore initial state
// {"207":"false",
// "208":"false",
// "209":"false",
// "210":"false",
// "211":"false",
// "212":"false",
// "213":"false",
// "214":"false"}
//
// when the "show/hide" comments button is clicked, it sets the id of all its children to "true" in the showMore state //variable, causing the css to //toggle it out of view.
const [showMore, setShowMore] = useState({});
const [rows, setRows] = useState({});
// "rows" is identical to "showMore" but is used when the "reply" button in clicked, causing the reply form appear.
//response from server containing all the comments and its nested replies etc..
const allComments = [
{
"id": 295,
"body": "This is a First level comment to the main Story blah blah...",
"created_at": "2021-07-16T17:17:10.410Z",
"updated_at": "2021-07-16T17:17:10.410Z",
"original_comment_author": null,
"parent_id": null,
"ancestry": null,
"date": "less than a minute",
"comment_number": 256,
"reply": false,
"user_id": 1,
"commentable_type": "Story",
"commentable_id": 1,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "Jimmy",
"comments": [
{
"id": 296,
"body": "this is the first reply to the main comment ",
"created_at": "2021-07-16T17:17:49.585Z",
"updated_at": "2021-07-16T17:17:49.585Z",
"original_comment_author": "undefined",
"parent_id": 295,
"ancestry": "295",
"date": "less than a minute",
"comment_number": 257,
"reply": true,
"user_id": 1,
"commentable_type": "Comment",
"commentable_id": 295,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "izzy",
"comments": [
{
"id": 298,
"body": "Reply to the reply (3rd level)",
"created_at": "2021-07-16T17:22:46.088Z",
"updated_at": "2021-07-16T17:22:46.088Z",
"original_comment_author": "undefined",
"parent_id": 296,
"ancestry": "295/296",
"date": "less than a minute",
"comment_number": 259,
"reply": true,
"user_id": 1,
"commentable_type": "Comment",
"commentable_id": 296,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "Noel",
"comments": [
{
"id": 299,
"body": "another reply to a reply (4th level) etc...",
"created_at": "2021-07-16T17:23:10.561Z",
"updated_at": "2021-07-16T17:23:10.561Z",
"original_comment_author": "undefined",
"parent_id": 298,
"ancestry": "295/296/298",
"date": "less than a minute",
"comment_number": 260,
"reply": true,
"user_id": 1,
"commentable_type": "Comment",
"commentable_id": 298,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "Mitch",
"comments": []
}
]
}
]
},
{
"id": 297,
"body": "this is a second reply to the main comment .... ",
"created_at": "2021-07-16T17:18:59.249Z",
"updated_at": "2021-07-16T17:18:59.249Z",
"original_comment_author": "undefined",
"parent_id": 295,
"ancestry": "295",
"date": "less than a minute",
"comment_number": 258,
"reply": true,
"user_id": 1,
"commentable_type": "Comment",
"commentable_id": 295,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "mike",
"comments": []
}
]
},
{
"id": 294,
"body": "This is another First Level comment to the main story ... ",
"created_at": "2021-07-16T17:16:19.314Z",
"updated_at": "2021-07-16T17:16:19.314Z",
"original_comment_author": null,
"parent_id": null,
"ancestry": null,
"date": "less than a minute",
"comment_number": 255,
"reply": false,
"user_id": 1,
"commentable_type": "Story",
"commentable_id": 1,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "Natalie",
"comments": []
}
]
;
useEffect( () => {
addAllCommentsToStateForReplyButtonToWork(allComments)
addAllCommentsToStateForShowMoreButtonToWork(allComments)
},[]);
const getReplyArray = (childrenCommentArray) => {
let tempArray = []
childrenCommentArray.map( (x, i) => {
x.id
tempArray.push(x.id + ", ")
});
return tempArray.length > 0 ? tempArray : "blank"
};
function addAllCommentsToStateForReplyButtonToWork(c) {
let newArray = [];
let newState = {};
function getAllId(arr, key) {
arr.forEach(function(item) {
for (let keys in item) {
if (keys === key) {
newArray.push(item[key])
} else if (Array.isArray(item[keys])) {
getAllId(item[keys], key);
}
}
});
}
getAllId(c, 'id');
newArray.forEach(function(item) {
newState[item] = "false";
})
setRows(newState);
}
function addAllCommentsToStateForShowMoreButtonToWork(c) {
let newArray = [];
let newState = {};
function getAllId(arr, key) {
arr.forEach(function(item) {
for (let keys in item) {
if (keys === key) {
newArray.push(item[key])
} else if (Array.isArray(item[keys])) {
getAllId(item[keys], key)
}
}
})
}
getAllId(c, 'id')
newArray.forEach(function(item) {
newState[item] = "false"
})
setShowMore(newState);
}
const handleSubmitClick = (e) => {
e.preventDefault()
}
const hideCommentsOrShowComments = (childrenCommentArray) => {
let tempArray = []
let numOfTrue = 0
let numOfFalse = 0
let tempShowMore = {}
childrenCommentArray.map( (x, i) => {
tempArray.push(x.id)
})
tempArray.forEach (x => {
if (showMore[x] == "true"){
numOfTrue = numOfTrue + 1
}else{
numOfFalse = numOfFalse + 1
}
})
if (numOfTrue > 0){
return "show replies"
}else{
return "hide replies"
}
}
const handleShowMoreButton = (childrenCommentArray) => {
//console.log("handleShowMoreButtonfrom article.jsx------------------------")
let tempArray = []
let tempShowMore = {}
childrenCommentArray.map( (x, i) => {
tempArray.push(x.id)
})
tempArray.forEach (x => {
//console.log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx idididid" + x)
if (showMore[x] == "true"){
//console.log("in if and x is = " + x + " and was true, changing it!")
tempShowMore[x] = "false"
}else{
//console.log("in else and x is = " + x + " and was false, changing it!")
tempShowMore[x] = "true"
}
} )
setShowMore({...showMore, ...tempShowMore})
}
const handleReplyButton = (id) => {
if (rows[id] == "true"){
setRows({...rows, [id]: "false"})
}else{
setRows({...rows,[id]: "true"})
}
}
const handleChange = () => {
//
}
////////////// Comment Function, Called recursively ///////////////
function Comment({ item, rows, showMore, handleShowMoreButton, handleReplyButton }){
const transition = useTransition(showMore[item.id], {
from: {opacity: 0},
enter: {y: 0, opacity: 1},
leave: {opacity: 0},
delay: 100
});
const nestedComments = (item.comments || []).map(com => {
return <Comment style={{border: "2px solid blue"}} key={com.id} item={com} type="child" rows={rows}
showMore={showMore} handleShowMoreButton={handleShowMoreButton}
handleReplyButton={handleReplyButton}/>
});
return (
<>
{transition((style,val) => val == "true" ? '' :
<CommentDisplay
style={style}
key={item.id + "commentDisplay"}
showMore={showMore}
item={item}
id={item.id} >
<BorderDiv/>
<TopBarWrapper>
<img src={item.author_avatar}/>
<h3 style={{alignSelf: "center", fontSize: ".6em", gridArea: "nick", marginRight: "8px"}}> {item.author_nick}
</h3>
<span style={{alignSelf: "center", gridArea: "date", fontSize: ".6em", color: "gray"}}></span>
</TopBarWrapper>
<CommentBody style={{gridArea: "body", fontSize: "15px"}}>
{item.body} (...the ID for this comment => {item.id}, and its children id's => {getReplyArray(item.comments)}) </CommentBody>
<BottomBarWrapper>
<Reply onClick={() => handleReplyButton(item.id)}>reply</Reply>
<VoteUp>
<svg viewBox="0 0 22 20" xmlns="http://www.w3.org/2000/svg"><path key={item.id + "path1"} data-id={ item.id + "path1"} d="M10.74.04a2.013 2.013 0 00-1.58 1.88c-.11 2.795-.485 4.45-2.283 6.946a1.272 1.272 0 00-1.065-.58h-4.55C.573 8.287 0 8.84 0 9.507v8.773c0 .667.572 1.218 1.263 1.218h4.55c.435 0 .821-.22 1.049-.548.263.204.506.387.758.533.417.24.887.384 1.532.45 1.29.128 3.403.032 8.283.052a.53.53 0 00.317-.113c1.224-.667 4.255-5.775 4.248-10.534-.026-1.138-.542-1.78-1.532-1.78H13.96c.388-2.47.131-4.738-.735-6.208C12.76.555 12.078.111 11.403.018a2.035 2.035 0 00-.663.022m2.154 7.912c-.055.28.201.58.498.58h6.934c.356.035.67.091.67.913 0 1.047-.168 2.886-1.031 5.057-.865 2.172-2.155 4.531-2.603 4.455-1.215.08-7.014.109-8.108 0-.556-.056-.818-.135-1.113-.306-.266-.152-.59-.423-1.066-.791v-7.6c2.349-2.88 2.979-5.302 3.096-8.3.338-1.495 1.702-1.082 2.179-.13.697 2.402.879 4.442.544 6.122M1.263 9.262h4.55c.148 0 .251.1.251.244v8.773c0 .144-.103.243-.252.243h-4.55c-.148 0-.251-.099-.251-.243V9.506c0-.144.103-.244.252-.244"></path>
</svg>
<span></span>
</VoteUp>
<VoteDown>
<svg viewBox="0 0 22 20" xmlns="http://www.w3.org/2000/svg"><path key={item.id + "path2"} data-id={ item.id + "path2"} d="M11.26 19.96a2.013 2.013 0 001.58-1.881c.11-2.794.484-4.45 2.282-6.945.224.345.618.58 1.066.58h4.548c.692 0 1.264-.553 1.264-1.22V1.722c0-.668-.572-1.22-1.264-1.22h-4.548c-.436 0-.823.22-1.05.55a6.898 6.898 0 00-.759-.534c-.416-.24-.887-.384-1.531-.45C11.558-.06 9.445.037 4.564.017a.521.521 0 00-.316.114C3.023.796-.007 5.904 0 10.663c.025 1.138.541 1.78 1.532 1.78H8.04c-.39 2.47-.131 4.738.735 6.208.467.794 1.148 1.238 1.823 1.331a2.034 2.034 0 00.663-.022m-2.155-7.913c.056-.28-.202-.579-.497-.579H1.674c-.356-.035-.67-.091-.67-.913 0-1.047.166-2.886 1.031-5.057C2.9 3.326 4.19.967 4.638 1.044c1.214-.081 7.014-.109 8.108 0 .556.055.818.134 1.113.305.265.152.59.423 1.066.791v7.6c-2.349 2.88-2.979 5.302-3.096 8.3-.338 1.495-1.702 1.083-2.179.13-.697-2.402-.88-4.442-.545-6.123m11.631-1.309h-4.548c-.149 0-.252-.1-.252-.244V1.722c0-.144.103-.244.252-.244h4.548c.15 0 .253.1.253.244v8.772c0 .144-.103.244-.253.244"></path> </svg>
<span></span>
</VoteDown>
<span style={{cursor: "pointer", marginLeft: "10px", fontSize: "10px", lineHeight: "40px"}}
onClick={() => handleShowMoreButton(item.comments)}>
{item.comments === undefined || item.comments.length == 0 ? "" : hideCommentsOrShowComments(item.comments)}
</span>
</BottomBarWrapper>
<FormWrapper rows={rows} commentid={item.id}>
<img src={item.author_avatar}></img>
<Form
id={item.id.toString() + "form"}
className="form-inline"
onSubmit={handleSubmitClick}
enctype="multipart/form-data" >
<div style={{width: "100%", height: "100%"}} className="field" >
<textarea
style={{width: "100%"}}
onChange={handleChange}
index={1}
placeholder={"...reply to " }
name="comment"
onKeyPress={e => {
if(e.key === 'Enter')
e.preventDefault()
}}
value={"enter a reply..."}/>
</div>
</Form>
<button
form={item.id.toString() + "form"}
style={{marginTop: "3px", gridArea: "main_comment_buttons"}}
type="submit">
reply now
</button>
</FormWrapper>
{nestedComments}
</CommentDisplay>
)}
</>
)
}
return (
<div style={{margin: "0 auto 90px auto"}}>
<div style={{background: "#d3f7b9"}}>
<ul style={{padding: "18px", margin: "0px 15px 0px 15px", width: "80vw"}}>
<li> <h2>Animation using React-spring useTransition not working as expected. All the divs get animated instead of only the respective divs when the "show/hide comments" button or the "reply" buttons are clicked. </h2></li>
<ul style={{margin: "10px 30px"}}>
<li>Each top level comment is mapped into the Comment function (Line 715) </li><br/>
<li>The Comment function (Line 548) recursively loops thru each top level comment's nested replies, replies of replies, replies of replies of replies etc.... adds useTansition to each and renders them. </li><br/>
<li> When the show/hide button is clicked, it changes the boolean state for all its children to true causing the children to collapse out of view. This is the part i want to animate. Same thing when the reply button is clicked, the reply form should be animated.</li><br/>
</ul>
</ul>
</div>
<div style={{position: "relative"}}>
{allComments.map( (c) => {
return (
<Comment
key={c.id}
item={c}
rows={rows}
showMore={showMore}
handleShowMoreButton={handleShowMoreButton}
handleReplyButton={handleReplyButton}/>
)
})}
</div>
</div>
)
}
class App extends React.Component {
render() {
return (
<CommentSection/>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
I ended up using an array of refs to animate the reply form and the show/hide button. It was all the setStates i had that were causing to many renders and didn't let the CSS transition animation to work.
Link to working solution
function CommentSection(props){
//all the comments from server, usually in useeffect hook but hardcoded here instead
const artDataComments = [
{
"id": 295,
"body": "This is a First level comment to the main Story blah blah...",
"created_at": "2021-07-16T17:17:10.410Z",
"updated_at": "2021-07-16T17:17:10.410Z",
"original_comment_author": null,
"parent_id": null,
"ancestry": null,
"date": "less than a minute",
"comment_number": 256,
"reply": false,
"user_id": 1,
"commentable_type": "Story",
"commentable_id": 1,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "Jimmy",
"comments": [
{
"id": 296,
"body": "this is the first reply to the main comment ",
"created_at": "2021-07-16T17:17:49.585Z",
"updated_at": "2021-07-16T17:17:49.585Z",
"original_comment_author": "undefined",
"parent_id": 295,
"ancestry": "295",
"date": "less than a minute",
"comment_number": 257,
"reply": true,
"user_id": 1,
"commentable_type": "Comment",
"commentable_id": 295,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "izzy",
"comments": [
{
"id": 298,
"body": "Reply to the reply (3rd level)",
"created_at": "2021-07-16T17:22:46.088Z",
"updated_at": "2021-07-16T17:22:46.088Z",
"original_comment_author": "undefined",
"parent_id": 296,
"ancestry": "295/296",
"date": "less than a minute",
"comment_number": 259,
"reply": true,
"user_id": 1,
"commentable_type": "Comment",
"commentable_id": 296,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "Noel",
"comments": [
{
"id": 299,
"body": "another reply to a reply (4th level) etc...",
"created_at": "2021-07-16T17:23:10.561Z",
"updated_at": "2021-07-16T17:23:10.561Z",
"original_comment_author": "undefined",
"parent_id": 298,
"ancestry": "295/296/298",
"date": "less than a minute",
"comment_number": 260,
"reply": true,
"user_id": 1,
"commentable_type": "Comment",
"commentable_id": 298,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "Mitch",
"comments": []
}
]
}
]
},
{
"id": 297,
"body": "this is a second reply to the main comment .... ",
"created_at": "2021-07-16T17:18:59.249Z",
"updated_at": "2021-07-16T17:18:59.249Z",
"original_comment_author": "undefined",
"parent_id": 295,
"ancestry": "295",
"date": "less than a minute",
"comment_number": 258,
"reply": true,
"user_id": 1,
"commentable_type": "Comment",
"commentable_id": 295,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "mike",
"comments": []
}
]
},
{
"id": 294,
"body": "This is another First Level comment to the main story ... ",
"created_at": "2021-07-16T17:16:19.314Z",
"updated_at": "2021-07-16T17:16:19.314Z",
"original_comment_author": null,
"parent_id": null,
"ancestry": null,
"date": "less than a minute",
"comment_number": 255,
"reply": false,
"user_id": 1,
"commentable_type": "Story",
"commentable_id": 1,
"edit_history": "",
"author_avatar": "undefined",
"author_nick": "Natalie",
"comments": []
}
]
;
const allShowMoreRefs = useRef([]);
allShowMoreRefs.current = []
const allReplyRefs = useRef([]);
allReplyRefs.current = []
const getReplyArray = (childrenCommentArray) => {
let tempArray = []
childrenCommentArray.map( (x, i) => {
x.id
tempArray.push(x.id + ", ")
})
return tempArray.length > 0 ? tempArray : "blank"
}
const handleReplyButton = (childrenCommentArray, e, itemID) => {
allReplyRefs.current.map ( (current, i) => {
if (itemID == current.id.substr(0, current.id.indexOf('-'))){
if (current.classList.contains("replyForm")){
current.classList.remove("replyForm")
}else{
current.classList.add("replyForm")
}
}
})
}
const handleShowMoreButton = (childrenCommentArray, e, itemID) => {
//set the label
if (e.target.innerText == "hide replies"){
e.target.innerText = "show replies"
}else{
e.target.innerText = "hide replies"
}
//get the ref(s) and change the css
childrenCommentArray.map(item => {
allShowMoreRefs.current.map ( (current, i) => {
if (item.id == current.id){
if (current.classList.contains("shrink")){
current.classList.remove("shrink")
}else{
current.classList.add("shrink")
}
}
})
})
}
//function called recursivley depended on how many nested comments get returned from server
const Comment = ({ item, userState, storyID, setArtDataComments, handleShowMoreButton, handleReplyButton}) => {
const addToShowMoreRefs = (el) => {
if (el && !allShowMoreRefs.current.includes(el)){
allShowMoreRefs.current.push(el)
}
}
const addToReplyRefs = (el) => {
//console.log("size b4 going in addToReplyeRefs is ", allReplyRefs.current.length )
console.log("in================= addTo_Reply_Refs")
if (el && !allReplyRefs.current.includes(el)){
//console.log("inside================= addToReplyeRefs")
//console.log(el)
allReplyRefs.current.push(el)
console.log("size after adding one is ", allReplyRefs.current.length )
}
}
const nestedComments = (item.comments || []).map(com => {
return (
<div key={item.id}>
<Comment style={{border: "2px solid blue"}}
item={com} type="child"
userState={userState}
storyID={storyID}
setArtDataComments={setArtDataComments}
handleShowMoreButton={handleShowMoreButton}
handleReplyButton={handleReplyButton} />
</div>
)
});
return (
<CommentDisplay
ref={addToShowMoreRefs}
className={"replies"}
key={item.id + "commentDisplay"}
item={item} id={item.id} >
<BorderDiv/>
<TopBarWrapper>
<img src="defaultAvatar"/>
<h3 style={{alignSelf: "center", fontSize: ".6em", gridArea: "nick", marginRight: "8px"}}>
{item.author_nick}
</h3>
<span style={{alignSelf: "center", gridArea: "date", fontSize: ".6em", color: "gray"}}></span>
</TopBarWrapper>
<CommentBody style={{gridArea: "body", fontSize: "15px"}}>
{item.body} this comment ID is {item.id} and its children array is {getReplyArray(item.comments)}
</CommentBody>
<BottomBarWrapper>
<Reply onClick={(e) => handleReplyButton(item.comments, e, item.id)}>reply</Reply>
<VoteUp onClick={(e)=>{handleVoteUp(e, item.id)}}>
<svg viewBox="0 0 22 20" xmlns="http://www.w3.org/2000/svg"><path key={item.id + "path1"} data-id={ item.id + "path1"}
d="M10.74.04a2.013 2.013 0 00-1.58 1.88c-.11 2.795-.485 4.45-2.283
6.946a1.272 1.272 0 00-1.065-.58h-4.55C.573 8.287 0 8.84 0 9.507v8.773c0 .667.572 1.218 1.263 1.218h4.55c.435 0 .821-.22 1.049-.548.263.204.506.387.758.533.417.24.887.384 1.532.45 1.29.128 3.403.032 8.283.052a.53.53 0 00.317-.113c1.224-.667 4.255-5.775 4.248-10.534-.026-1.138-.542-1.78-1.532-1.78H13.96c.388-2.47.131-4.738-.735-6.208C12.76.555 12.078.111 11.403.018a2.035 2.035 0 00-.663.022m2.154 7.912c-.055.28.201.58.498.58h6.934c.356.035.67.091.67.913 0 1.047-.168 2.886-1.031 5.057-.865 2.172-2.155 4.531-2.603 4.455-1.215.08-7.014.109-8.108 0-.556-.056-.818-.135-1.113-.306-.266-.152-.59-.423-1.066-.791v-7.6c2.349-2.88 2.979-5.302 3.096-8.3.338-1.495 1.702-1.082 2.179-.13.697 2.402.879 4.442.544 6.122M1.263 9.262h4.55c.148 0 .251.1.251.244v8.773c0 .144-.103.243-.252.243h-4.55c-.148 0-.251-.099-.251-.243V9.506c0-.144.103-.244.252-.244"></path></svg>
<span commentid={item.id}>{item.total_upvotes}</span>
</VoteUp>
<VoteDown onClick={(e)=>{handleVoteDown(e, item.id)}}>
<svg viewBox="0 0 22 20" xmlns="http://www.w3.org/2000/svg"><path key={item.id + "path2"} data-id={ item.id + "path2"} d="M11.26 19.96a2.013 2.013 0 001.58-1.881c.11-2.794.484-4.45 2.282-6.945.224.345.618.58 1.066.58h4.548c.692 0 1.264-.553 1.264-1.22V1.722c0-.668-.572-1.22-1.264-1.22h-4.548c-.436 0-.823.22-1.05.55a6.898 6.898 0 00-.759-.534c-.416-.24-.887-.384-1.531-.45C11.558-.06 9.445.037 4.564.017a.521.521 0 00-.316.114C3.023.796-.007 5.904 0 10.663c.025 1.138.541 1.78 1.532 1.78H8.04c-.39 2.47-.131 4.738.735 6.208.467.794 1.148 1.238 1.823 1.331a2.034 2.034 0 00.663-.022m-2.155-7.913c.056-.28-.202-.579-.497-.579H1.674c-.356-.035-.67-.091-.67-.913 0-1.047.166-2.886 1.031-5.057C2.9 3.326 4.19.967 4.638 1.044c1.214-.081 7.014-.109 8.108 0 .556.055.818.134 1.113.305.265.152.59.423 1.066.791v7.6c-2.349 2.88-2.979 5.302-3.096 8.3-.338 1.495-1.702 1.083-2.179.13-.697-2.402-.88-4.442-.545-6.123m11.631-1.309h-4.548c-.149 0-.252-.1-.252-.244V1.722c0-.144.103-.244.252-.244h4.548c.15 0 .253.1.253.244v8.772c0 .144-.103.244-.253.244"></path></svg>
<span commentid={item.id}>{item.total_downvotes}</span>
</VoteDown>
<span
style={{cursor: "pointer", marginLeft: "10px", fontSize: "10px", lineHeight: "40px"}}
onClick={(e) => handleShowMoreButton(item.comments, e, item.id)}>
{item.comments === undefined || item.comments.length == 0 ? "" : "hide replies"}
</span>
</BottomBarWrapper>
<div
id={item.id + "-replyform"}
className={"replyFormHidden"}
ref={props.addToReplyRefs}
commentid={props.commentid}
ref={addToReplyRefs}
originalcommentAuthor={item.author_nick}
userState={userState}
storyID={storyID}
commentid={item.id}
setArtDataComments={setArtDataComments}
handleReplyButton={handleReplyButton}>
<img src="defaultManIcon"></img>
<Form
id={item.id + "form"}
className="form-inline"
onSubmit="handleAdd"
enctype="multipart/form-data" >
<div style={{width: "100%", height: "100%"}} className="field" >
<Textarea
type="textarea"
className="form-control"
index={1}
placeholder={"...reply to " + props.commentAuthor}
name="comment"
onKeyPress={e => {
if(e.key === 'Enter')
e.preventDefault()
}}/>
</div>
</Form>
<button
form={item.id + "form"}
style={{marginTop: "3px", gridArea: "main_comment_buttons"}}
type="submit" >
reply now
</button>
</div>
{nestedComments}
</CommentDisplay>
)
}
return (
<Comments>
<div>
<div style={{position: "relative"}}>
{
artDataComments.map( (c, i) => {
return (
<div key={c.id}>
<Comment
item={c}
userState={props.userState}
handleShowMoreButton={handleShowMoreButton}
handleReplyButton={handleReplyButton} />
</div>
)
})
}
</div>
</div>
</Comments>
);
}

How to display chat messages on left and right in react-native-gifted-chat

I have to load previous chat messages first and i am getting response of messages like this
{
"id": "8102f902-d54b-457a-823e-13d3sssssds",
"buyerID": "4b358200-4da0-46a0-ad8b-838888888848",
"sellerID": "cf82474c-277b-483e-88ce-77777777777",
"text": "Hello, I would like to learn how to contour my cheeks.",
"messageType": "TEXT",
"createdAt": "2021-01-02T22:51:11.220Z",
"senderType": "BUYER",
"updatedAt": "2021-01-02T22:51:11.220Z"
},
{
"id": "08a8e684-2279-4cc1-9d2d-d5e4ebd9210a",
"buyerID": "4b358200-4da0-46a0-ad8b-838888888848",
"sellerID": "cf82474c-277b-483e-88ce-77777777777",
"text": "Sure how can I help contour?",
"messageType": "TEXT",
"createdAt": "2021-01-02T22:56:49.019Z",
"senderType": "SELLER",
"updatedAt": "2021-01-02T22:56:49.019Z"
},
{
"id": "67d75630-245a-46d4-9d33-8812d1e48b68",
"buyerID": "4b358200-4da0-46a0-ad8b-838888888848",
"sellerID": "cf82474c-277b-483e-88ce-77777777777",
"text": "Yo tell me how I can help",
"messageType": "TEXT",
"createdAt": "2021-01-02T23:04:39.893Z",
"senderType": "SELLER",
"updatedAt": "2021-01-02T23:04:39.893Z"
},
but all the messages are displaying on the left side of screen but i want buyer messages on the right side
I have including giftedChat component like this
<GiftedChat
messages={messages}
onSend={messages => onSend(messages)}
/>
because response doesn't have users avatar i also want to display avatar from custom url but that is also not working i am first time using gifted chat
can anybody help me how to do it ?
or any suggestions
Thanks in advance
I found solution for it
I changed the format of messages into required format of gifted chat it will not work fine until we modify our response into the gifted chat required format
Here it is what i did
let filteredChatMessages = yourMessages.map((chatMessage) => {
let modifiedMessages = {
_id: chatMessage.user_id,
text: chatMessage.text,// or chatMessage.message
createdAt: chatMessage.createdAt,
user: {
_id: chatMessage.id
avatar: chatMesssage.user_img_url
}
};
return modifiedMessages;
});
console.log(filteredChatMessage);
Then render bubble
// Render bubble
const renderBubble = props => {
const message_sender_id = props.currentMessage.user._id;
return (
<Bubble
{...props}
position={message_sender_id == currentLoggeduser ? 'right' : 'left'}
textStyle={{
right: {
color: COLORS.white,
fontSize: SIZES.s3
},
left: {
fontSize: SIZES.s3,
},
}}
wrapperStyle={{
right: {
backgroundColor: COLORS.appViolet,
marginRight: 5,
marginVertical: 5
},
left: {
marginVertical: 5
},
}}
/>
);
};
and finally render your gifted chat component
<GiftedChat
messages={messages}
onSend={messages => onSend(messages)}
renderBubble={renderBubble}
showUserAvatar={true}
scrollToBottom={true}
scrollToBottomComponent={renderScrollToBottom}
text={textMessage}
onInputTextChanged={text => setTextMessage(text)}
inverted={false}
/>

How to show first letters of the firstname and lastname of the user in the list instead of image through json data using reactjs

I'm new to reactjs, doing a small react app which should hold first letter of User's firstname and lastname using json data in reactjs. but i'm unable to do it... I getting functions for static data but not for dynamic.
json data:
[
{
"id": 1,
"firstname": "Avelino",
"lastname": "Cajes",
"position": ".Net Developer",
},
{
"id": 2,
"firstname": "Nilson",
"lastname": "Audrey",
"position": "C#",
}
]
Can anyone help me to do this problem? Thanks in advance.
I want in this way
Install react-avatar:
npm install react-avatar
Then try adapting this code to suit your use case
import React from "react";
import Avatar from 'react-avatar';
let data = [
{ "id": 1, "firstname": "Avelino", "lastname": "Cajes", "position": ".Net Developer"},
{ "id": 2, "firstname": "Nilson", "lastname": "Audrey", "position": "C#", }
]
class MyComponent extends React.Component{
state = {data}
render(){
return(
<div>
{ this.state.data.map(item=>(
<Avatar name={item.firstname+" "+item.lastname} maxInitials={2}/>
))
}
</div>
)
}
}
You can find out more on how to use react-avatar here
I think this is what u asked for
pass this to text element: firstName.charAt(0).toUpperCase() +""+lastName.charAt(0).toUpperCase()
import { LinearGradient } from 'expo-linear-gradient';
<LinearGradient colors={['#4c669f', '#3b5998', '#192f6a']} style=
{styles.linearGradient}>
<Text style={styles.buttonText}>
</Text>
</LinearGradient>
// Later on in your styles..
var styles = StyleSheet.create({
linearGradient: {
flex: 1,
paddingLeft: 15,
paddingRight: 15,
borderRadius: 5
},
buttonText: {
fontSize: 18,
fontFamily: 'Gill Sans',
textAlign: 'center',
margin: 10,
color: '#ffffff',
backgroundColor: 'transparent',
},
});

Not getting tree with expected link style in ReactJS using D3

I'm pretty new to D3.js library. I've been trying to implement it in React JS project.
I'm just using d3 functions for calculations and rendering part will be done by React JS.
I successfully created a tree but I'm getting links with weird style (Not as expected)
This is what I expected (As per D3 Documentation)
But I got the below tree :(
Also I can't able to collapse or expand...
You can find the code below.
componentDidMount() {
const width = 800, height = 800;
const tree = d3.tree().size([height, width - 160]);
const stratify = d3.stratify().id((d) => {
return d.name;
}).parentId((d) => {
return d.parent;
});
const root = stratify(this.state.data)
.sort((a, b) => {
return (a.height - b.height) || a.id.localeCompare(b.id);
});
this.setState({ paths: tree(root).links() });
this.setState({ nodes: root.descendants() })
}
render() {
let paths = this.state.paths && this.state.paths.map(item => {
let d = d3
.linkHorizontal()
.x((d) => {
return d.y;
})
.y((d) => {
return d.x;
});
return <path className='link' d={d(item)} />
})
let nodes = this.state.nodes && this.state.nodes.map((node, i) => {
return <g key={node.id} className={"node" + node.children ? " node--internal" : " node--leaf"}
transform={`translate(${node.y}, ${node.x})`}>
<circle r="10" style={{ 'fill': node.children ? 'lightsteelblue' : 'black' }} />
<text y="0" dy="0" textAnchor="middle"
style={{ 'fillOpacity': 1 }}>{node.name}</text>
</g>
})
return (
<svg className="tree-chart-basic" ref={(r) => this.chartRf = r} style={{ width: '800px', height: '800px' }}>
<g transform='translate(20,20)'>
{nodes}
{paths}
</g>
</svg>
);
}
this.state.data will be having the array as follows
[
{ "name": "ProjectA", "parent": "" },
{ "name": "ApplicationA", "parent": "ProjectA" },
{ "name": "EnvironmentB", "parent": "ProjectA" },
{ "name": "TierC", "parent": "ApplicationA" },
{ "name": "TierD", "parent": "ApplicationA" },
{ "name": "TierE", "parent": "ApplicationA" },
{ "name": "ServiceF", "parent": "EnvironmentB" },
{ "name": "ContainerG", "parent": "EnvironmentB" },
{ "name": "ContainerH", "parent": "TierE" },
{ "name": "ContainerH", "parent": "TierE" },
{ "name": "ContainerH", "parent": "TierE" },
{ "name": "ContainerH", "parent": "TierE" },
{ "name": "ContainerH", "parent": "TierE" },
{ "name": "ContainerH", "parent": "TierE" }
]
How can I get the expected Tree in my code?
How can I get collapse/expand functionality?
Please tell me what I'm doing wrong. Thanks for the time. (:
I finally got the solution to my answer.
I need to specify the style to <path> tag as follows.
<path
fill="none"
stroke="#97a6ff"
strokeWidth="2px"
/>
I had the same issue when using react-tree-graph which uses d3 under the hood. Luckily you can add custom classes to customize everything. I use css-modules so I had to escape the global classnames.
import { Tree } from 'react-tree-graph';
import style from './tree.module.scss';
// ...
<Tree svgProps={{ className: style.acmeProductTree }} />
// tree.module.scss
svg.acmeProductTree path:global(.link) {
fill: none;
stroke: #2593b8;
stroke-width: 1.5px;
}
As I found out just now you can alternatively apply a custom class directly to the path element:
<Tree pathProps={{ className: style.acmeProductTreeLink }} />
.acmeProductTreeLink {
fill: none;
stroke: #2593b8;
stroke-width: 1.5px;
}

Resources