Reactjs: Replying comments not working in reactjs - reactjs

Replying comments not working in reactjs.
Believe I have search through this venerable forum but most solution found could not resolve my issue.
This code works fine by displaying post and its corresponding comments from an array. Great Thanks to Stackoverflow Engineer Ryan for his help. Now I want to display each reply made on each comments as per code below
{props.comment.replys.map((reply, i) => (<div>{reply.reply}</div>))}
but it shows error
Cannot read property 'replys' of undefined
at Post
In angularjs I can just implement code below inside Post ng-repeat function.
<div class="post" ng-repeat='post in posts'>
<div>
{{ post.content }}
</div>
<!-- Comments -->
<div ng-repeat='comment in post.comments'>
<div class='comment'>{{ comment.comment }} <b>{{ comment.id }}</b></div>
<div ng-repeat='reply in comment.replys'>
<div>{{ reply.reply }}</div></div>
</div>
</div>
Here is the updated code in reactjs.
import React, { Component, Fragment } from "react";
import { render } from "react-dom";
const Post = (props) => {
console.log(props.post);
console.log(props.comments);
return (<React.Fragment><li >
{props.post.id} - {props.post.content}
</li>
<div>
{props.post.comments.map((comment, i) => (<div>{comment.comment} --- {comment.id}</div>))}
{props.comment.replys && props.comment.replys.map((reply, i) => (<div>{reply.reply}</div>))}
</div>
</React.Fragment>
);
};
class Comment extends React.Component {
constructor(props) {
super(props);
this.state = {
rec: [
{"id":"1","content":"first post","comments":[{"comid":"1","comment":"first comment","replys":[{"reply":"first comment reply1"},{"reply":"first comment second reply"}] }]},
{"id":"2","content":"second post","comments":[{"comid":"2","comment":"second comment", "replys":[{"reply":"second comment reply1"}] }]}
],
};
}
render() {
return (
<div>
<h3>Records</h3>
<ul>
{this.state.rec.map((post, i) => (
<Post post={post} key={i}/>
))}
</ul>
</div>
);
}
}

const Post = (props) => {
return (<React.Fragment>
<li>
{props.post.id} - {props.post.content}
</li>
<div>
{props.post.comments.map((comment, i) => (
<div key={"comment_" + comment.id}>
<div>{comment.comment} --- {comment.id}</div>
{comment.replys && comment.replys.map((reply, i) => (<div>{reply.reply}</div>))}// Here
</div>
))}
</div>
</React.Fragment>
);
};
You can take replays data in this way.

One comment on your second post in the sample data has no replies (replys) so it's quite expected that you see that error: you call map on undefined.
Try this instead (check if we have replies first and only map if some exist):
{props.comment.replys && props.comment.replys.map((reply, i) => (<div>{reply.reply}</div>))}
Edit
Here's one implementation of the Post method (I renamed it to renderPosts, you should do the same) that should work (better):
const renderPosts = (props) => {
return (
<>
<li>
{props.post.id} - {props.post.content}
</li>
<div>
{props.post.comments.map((comment) => {
return (
<div key={comment.comid}>
<div>{comment.comment} --- {comment.id}</div>
{comment.replys && comment.replys.map((reply) => <div key={reply.reply}>{reply.reply}</div>)}
</div>
)
})}
</div>
</>
);
};
Note that I've:
- replaced the outer element with the shorter notation for fragments
- moved the iteration of the replies inside of the iteration of the comments (each comment has a list of replies)
- added the mandatory key attribute (you should add an id to the replies on your datamodel, I used the reply.reply value for the moment but that's in general a bad id, because there's no guarantee that all replies are different)
Hope it helps!

Related

React - Each child in a list should have a unique 'key' prop

As my first react project, I decided to try and make a Pokedex.
I have an array of Pokemon and that I pass into a List component and use the .map() function to render. I understand that the root-most element of that the .map() function returns needs a unique key and I understand that it is ideal if the key can be truly unique to the item so that if the list is sorted, items have the same key. Therefore, I figured using the 'id' of the pokemon would be ideal. I believe I have done that but I cannot get rid of the warning in console. I'd really appreciate a hand with this.
export default class List extends React.Component {
render() {
const { list, nav } = this.props;
return (
<div className="list">
{list.map((pokemon) => (
<PokemonItem key={pokemon.id} navigation={nav} pokemon={pokemon} />
))}
</div>
);
}
}
PokemonItem Render Method
render() {
const { pokemon, navigation } = this.props;
return (
<div onClick={() => {
navigation.navigate("Details", { pokemon });
}}
className={"list-item bg_" + pokemon.types[0]}>
<div className="header">
<div className="name">{pokemon.name}</div>
<div className="id">#{this.getId(pokemon.id)}</div>
</div>
<div className="body">
<div className="types">
{pokemon.types.map((type) => {
return <div className="type">{type}</div>;
})}
</div>
<div className="sprite">
<img src={pokemon.imgURL} alt={pokemon.name} title={pokemon.name}></img>
</div>
</div>
<div className="background-image">
<img src={PkBall} alt="" />
</div>
</div>
);
}
Warning message showing in console
Checking your PokemonItem it reveals that the reason may be laying in this piece of code:
{pokemon.types.map((type) => {
return <div className="type">{type}</div>;
})}
This is easily fixed by adding the key attribute:
{pokemon.types.map((type) => {
return <div className="type" key={type.id}>{type}</div>;
})}
You need to add a key in every item returned from a map in order to avoid this error. Also I advice you to add the console output related to your question in the body so it's easier to pinpoint the errors.
After the edit of the OP's question the warning occurs here:
<div className="types">
{pokemon.types.map((type) => {
return <div className="type">{type}</div>;
})}
</div>
The key-property is not set for div and should be done like in the first method. If type is unique you can use this as key.

Problem rendering the contents of array using map on reactjs

I use axios to get the data from the server and store the received data into the array setCountries. This part works.
Code in codesandbox
Then, I simply want to render the whole list of country names contained on the array using map.
I am making some mistake there, because I get the error
TypeError: setCountries.map is not a function
The error comes from this part of the code.
Where is the error coming from?
const [countries, setCountries] = useState([])
const showCountries = () => {
return (
<div>
<ul>
{setCountries.map((country) =>
<p key={country.alpha2Code}>{country.name}</p>
)}
</ul>
</div>
)
}
return (
<div>
<div>
<h1>Countries</h1>
{showCountries()}
</div>
</div>
);
}
export default App;
You're using function setCountries instead of array countries
Corrected:
<ul>
{countries.length > 0 && countries.map((country) =>
<p key={country.alpha2Code}>{country.name}</p>
)}
</ul>
Also use null checks to avoid other issues
setCountries is a function for setting the country's state. It does not have the map function.
The solution here will be to use countries.map instead:
const showCountries = () => {
return (
<div>
<ul>
{countries.map((country) =>
<p key={country.alpha2Code}>{country.name}</p>
)}
</ul>
</div>
)
}
setCountries is a function and that's why you can't use a map. If you try the code I wrote below, your problem will be resolved.
{countries.map((country) =>
<p key={country.alpha2Code}>{country.name}</p>
)}

how to render divs dynamically based on the number of items

I want to show slideshow circle buttons dynamically based on the number of slides I will get from the server. I couldn't do it with loops since I have a number of slides predefined & no array. How can I show my buttons? Now I manually created 4 buttons. (I deleted unnecessary code so that only important snippets are present).
import React = require("react");
type NavigationEvent = "Backward" | "Forward" | "Pause" | "Play";
interface Props {
slidesAmount: number;
activeSlideIndex: number;
}
function SlideshowCarouselNavigationBarComponent(props: Props) {
const onPlayButtonClicked = () => {
props.navigate("Play");
}
const onPauseButtonClicked = () => {
props.navigate("Pause");
}
const onSlideButtonClicked = index => {
props.navigateToIndex(index);
}
return (
<div>
<div>
<div className={classes.dotsContainer}>
<div className={classes.dots}>
</div>
<div className={classes.dots}>
</div>
<div className={classes.dots}>
</div>
<div className={classes.dots}>
</div>
</div>
</div>
</div>
)
}
export = SlideshowCarouselNavigationBarComponent;
You can do the following (it is the most concise and understable way that I found out) : Iterating slidesAmount times, in order to display the circles:
<div className={classes.dotsContainer}>
{[...Array(props.slidesAmmount)].map((_)=>(
<div className={classes.dots}>
</div>
))}
</div>
You can use Array.fill() This will create array of preidentified length filled with unidentified values
new Array(props.slidesAmmount).fill().map((v, i){
return <div> .... </div>
});
please see How to create an array containing 1...N for more detailed answers

How to use simulate(click) function in react unit test

I am beginner level in React test.
I have a component like a small carousel. I want to test if change state(currentPage) when i click. But i didn't. It throws that error message:
Method “simulate” is only meant to be run on a single node. 0 found
instead.
I didnt understand " what is node,which node ? "
That is my test code :
it("check if next button works properly", () => {
const wrapper = shallow(<QuickStartGuide />);
const nextButton = wrapper.find(".quick-start-guide-messages-control-button .next-button");
nextButton.simulate('click');
expect(wrapper.state().currentIndex).toEqual(1);
})
And component :
<div className={containerClassName}>
{isOpen &&
<div className={"quick-start-guide-content-wrapper"}>
<div className="quick-start-guide-header">
<MtButton text={"MINIMIZE"}
hasIcon={true}
onClick={this.handleTriggerButtonClick}
className={"quick-start-guide-minimize-button"}/>
<div className={"quick-start-guide-title"}>{"Quick Start Guide"}</div>
{!!guideMessages.length &&
<Fragment>
<div className={"quick-start-guide-messages"}>
<MtButton className={"quick-start-guide-messages-control-button prev-button"}
hasIcon={true}
onClick={this.handleGoToPrevMessageButtonClick}
isDisabled={!currentGuideMessageIndex}
text={"PREV"}/>
{guideMessages[currentGuideMessageIndex][0]}
<MtButton className={"quick-start-guide-messages-control-button next-button"}
hasIcon={true}
onClick={this.handleGoToNextMessageButtonClick}
isDisabled={currentGuideMessageIndex >= guideMessages.length - 1}
text={"NEXT"}/>
</div>
<div className={"quick-start-guide-indicators"}>
{guideMessages.map((item, index) => (
<IndicatorItem key={item[0].key}
order={index}
onClick={this.handleIndicatorItemClick}
className={classNames({
active: index === currentGuideMessageIndex
})}/>
))}
</div>
</Fragment>}
</div>
<div className={"quick-start-guide-content"}>
<div className={"quick-start-guide-action-buttons"}>
{guideActions.map(([text, handleActionButtonClick, isDisabled = false]) => (
<MtButton key={text}
text={text}
isDisabled={isDisabled}
visualType={BUTTON_VISUAL_TYPES.ROUNDED}
className={"quick-start-guide-action-button"}
onClick={handleActionButtonClick}/>
))}
</div>
</div>
</div>}
<MtButton className={"uick-start-guide-trigger-button"}
text={"GUIDE"}
onClick={this.handleTriggerButtonClick}
hasIcon={true}/>
</div>
The error you're receiving isn't related to the simulate(click) and is an issue with your selector because it's not finding anything to actually click.
Double check that this is the correct selector to use: wrapper.find(".quick-start-guide-messages-control-button .next-button");.
You can use console.log(wrapper.debug()) to check to see what elements are available to select.

How to render my Modal window and all the information contained inside ( in React)?

My application renders twelve random people fetched from a different website. Everything works fine apart from my modal component(it should render more information about the person you clicked). For some reason whenever I try to render it I get this error 'Modal.js:9 Uncaught TypeError: Cannot read property 'medium' of undefined' and more errors comes with it. I am printing props.modalInfo from the Modal component to the console and it does have all the information I need, but for some reasons it shows that props.modalInfo is undefined when I try to render it. I have never done modal box in React (I am a beginner). Could someone explain me how I can render my Modal and pass all the data successfully? Thank you in advance!
handleClick(id) {
this.setState((prevState) => {
const modalInfoToPass = prevState.employeeList.filter(employee =>
{
if(`${employee.name.first} ${employee.name.last}` === id){
// get only and only one object that fulfils the
// condition
return employee;
}
})
return {
displayModal: true,
// update the modalInfo state
modalInfo: modalInfoToPass
}
})
}
render(){
return (
<div className='container'>
<Header />
<main>
{
this.state.loading ? <h2 className='load-page'>Loading...</h2> :
this.state.employeeList.map(employee =>
<Employee key={`${employee.name.title}
${employee.name.last}`}
employeeInfo={employee}
**handleClick={this.handleClick}**
/>)
}
</main>
<Footer />
**{this.state.displayModal && <Modal modalInfo={this.state.modalInfo} />}**
</div>
);
}
function Modal(props) {
**console.log(props.modalInfo);**
return (
<div className='bg-modal'>
<div className='modal-content'>
<div className='modal-image'>
<img src={props.modalInfo.picture.medium} alt={`${props.modalInfo.name.title} ${props.modalInfo.name.first}`}/>
</div>
<div className='modal-info'>
<p className='name'>{props.modalInfo.name.first} {props.modalInfo.name.last}</p>
<p className='email'>{props.modalInfo.email}</p>
<p className='place'>{props.modalInfo.location.city}</p>
</div>
<hr />
<div className='modal-more-info'>
<p className='number'>{props.modalInfo.cell}</p>
<p className='address'>{`${props.modalInfo.location.street}, ${props.modalInfo.location.state}`}</p>
<p className='postcode'>{props.modalInfo.location.postcode}</p>
<p className='birthday'>{props.modalInfo.dob.date}</p>
</div>
</div>
</div>
);
}
What is id and is it on an employee? If it isn't available, you could just pass what you're filtering for in your handleClick:
handleClick={()=>this.handleClick(`${employee.name.first} ${employee.name.last}`)}
Or, you could just pass the employee:
handleClick={()=>this.handleClick(employee)}
and modify your handler:
handleClick(employee) {
this.setState({modalInfo: employee, displayModal: true})
}

Resources