Calling an External Function in a React Component - reactjs

I have a need, in a site I'm building, for a list component that is reused several times. However, the list is purely for rendering and is not responsible for the state of the app at all. I know you either cannot, or are not supposed to have dumb components containing any logic, but I am not sure how to proceed without using a smart component, which is entirely unnecessary. Here is my smart component that works:
class Menu extends Component {
renderItems(items) {
return this.props.items.map((i, index) => {
return (
<li key={index} style={{marginLeft: 10}}>
{i}
</li>
)
});
}
render() {
const { listStyle } = styles;
return (
<div>
<ul style={listStyle}>
{this.renderItems()}
</ul>
</div>
)
}
}
And I've tried this:
function Menu(props) {
return props.items.map((i, index) => {
<li key={index} style={{marginLeft: 10}}>
{i}
</li>
});
}
And then calling it inside Nav like this, which does not throw an error but does not render anything from menu either:
const Nav = () => {
const { listStyle, containerStyle } = styles;
return (
<div style={containerStyle}>
<Logo url={'#'}
src={PickAPlayLogo}
width={300} />
<Menu items={pageLinks} />
<Menu items={socialMediaLinks} />
<Logo url={'#'}
src={AppStoreLogo}
width={170} />
</div>
);
};
Also, worth noting, I have never come across a function that is supposed to be rendered like a component, but was trying it based on the example on this page

Heres an answer similar to what you have going on
function Menu(props) {
this.renderItems = () => {
return (
<ul>
{props.items.map((i, index) => {
return (
<li>{i}</li>
)
})}
</ul
)
}
return(
this.renderItems()
)
}

Here we go:
function Menu(props) {
const {listStyle} = styles;
const listItems = props.items.map((i, index) =>
<li key={index} style={{marginLeft: 10}}>
{i}
</li>
);
return (
<ul style={listStyle}>{listItems}</ul>
);
}

Related

react.js error handling inside functional component

I have a component which receives a list of items through props.
It looks like this:
const component = (props) => {
return (
<ul>
{props.list.map((item) => (
<ListItem key={item.Id} title={item.title} imgSrc={item.img.url} />
))}
</ul>
);
};
edit:
and the child looks like this:
const ListItem = (props) => {
return (
<li key={props.key}>
<h4>{props.title}</h4>
<div>
<img src={props.imgSrc} alt='thumbnail'
/>
</div>
</li>
);
};
The list comes from an API and there are cases in which the values I am assigning will be undefined or not available (imgSrc for example). This breaks the entire rendering of the app.
How can I handle errors in a way that will skip the problematic item and continue with the mapping? It usually means this is a deleted item so I wish to skip it all together.
I usually wrap the code with a try-catch or if statement but I am not allowed to do it here.
There are many options to solve that. For example, you could use the filter method before your .map call.
const component = (props) => {
return (
<ul>
{props.list.filter((item) => item.img.url !== undefined).map((item) => (
<ListItem key={item.Id} title={item.title} imgSrc={item.img.url} />
))}
</ul>
);
};
Another possible option could be Error Boundaries. I don't think that they are what you need, but it could be interesting for you anyways.
You can conditional rendering.
Array.isArray(props.list) &&
props.list.map((item) => (
<ListItem key={item.Id} title={item.title} imgSrc={item.img.url} />
));
You can only map over the array if it is an array as:
const component = (props) => {
return (
<ul>
{Array.isArray(props.list) && props.list.map((item) => (
<ListItem key={item.Id} title={item.title} imgSrc={item.img.url} />
))}
</ul>
);
};

how can I add any event to a specific part of component ? react

I have list of data that render it with map - I need to add an event just in one of the item from that list.
const UserModal = (props) => {
const {user,setUser} = props ;
const list = [,{id:3,text:'گفت وگو ها',icon:<BsChat />},{id:5,text:'خروج',icon:<BiExit />},];
/this is my list for making navigation bar
return (
<div className={style.main}>
<div style={{bordeBottom:'1px solid black'}}>
<BiUser />
<p>{user.username}</p>
</div>
{ //this is where I render a list to show and make component
list.map((item)=>
<div key={item.id}>
{item.icon}
<p>{item.text}</p>
</div>)
}
</div>
);
};
export default UserModal;
this my code and for example I need to add an event on specific object that has id=5 in that list .
how can I do that
I don't know if there is some sort of built-in solution for this, but here is a simple workaround:
I changed a few things for simplicity's sake
The important part is the if statement with checks if item ID is 5 then if so adds a div with the desired event
function App() {
const list = [
,
{ id: 3, text: "comonent 3" },
{ id: 5, text: "comonent 5 (target)" }
];
return (
<>
<h1>Hello world<h1/>
{list.map((item) => (
<div key={item.id} style={{ backgroundColor: "red" }}>
<p>{item.text}</p>
{item.id == 5 ? (
<div
onClick={() => {
alert("This component has a event");
}}
>
{" "}
event
</div>
) : (
<></>
)}
</div>
))}
</>
);
}
const UserModal = (props) => {
const {user,setUser} = props ;
const myEvent = () => alert('event fired');
const list = [,{id:3,text:'گفت وگو ها',icon:<BsChat /> , event : myEvent},{id:5,text:'خروج',icon:<BiExit />},];
/this is my list for making navigation bar
return (
<div className={style.main}>
<div style={{bordeBottom:'1px solid black'}}>
<BiUser />
<p>{user.username}</p>
</div>
{ //this is where I render a list to show and make component
list.map((item)=>
<div key={item.id}>
{item.icon}
<p onClick={item.event}>{item.text}</p>
</div>)
}
</div>
);
};
export default UserModal;
list.map((item, i)=> (
item.id == 5 ?
<div onClick={handleClick} key={i}></div>
:
<div key={i}></div>
)

How can I get elements from one component in another one by id or value?

I've created a component to create follow and unfollow buttons and now I want to use this component in other components (like Suggestions).
In the suggestions component I want to show only the button that its value is equal to the user.id, but I am only able to get the 5 buttons from the original component.
Is there a way to select only the button that is equal to the user.id?
This is the component that creates the buttons:
render() {
const { users, followingUsers } = this.state
const userId = this.props.user[0].id
return(
<div>
{users.map((user, index) => {
if(userId !== user.id) {
if(followingUsers.includes(user.user_name)) {
return(
<Button key={index} value={user.id} onClick={this.onUnfollow}>Unfollow</Button>
)
} else {
return(
<Button key={index} value={user.id} onClick={this.onFollow}>Follow</Button>
)
}
}
})}
</div>
)
}
}
export default withUser(Unfollowfollow);
Here is the suggestions component:
render() {
const { users } = this.state
const userId = this.props.user[0].id
return (
<div>
<ul>
{users.map((user, index) => {
if(user.id !== userId) {
return (
<Card className="users" key= {index}>
<CardBody>
<CardImg className="picfollowers" top width="9%" src={user.image} />
<CardTitle onClick={() => this.handleClick(user.id)}>{user.user_name}</CardTitle>
<Unfollowfollow />
</CardBody>
</Card>
)}
})}
</ul>
</div>
)
}
}
export default withUser(Suggestions);

Combine API results from multiple arrays into a single array in a React.js component

I have the following React.js component where I get 10 multiple-choice trivia questions via an API call using fetch and recursively list them on the page via nested components.
The API provides 'correct_answer' as string, and 'incorrect_answers' separately as an array of strings. In my current code, I am only able to list the 'correct' and 'incorrect' answers in their own components.
What I would like to do is combine the 'correct' and 'incorrect' answers into a single array and then randomise the output of them, so that the correct answer is not always in the same place in the list. How would I alter my current code to that? I am an absolute beginner at React.js so any pointers are welcome, thanks.
import React, { Component } from "react";
class QuestionContainer extends Component {
constructor() {
super();
this.state = {
questions: []
};
}
componentWillMount() {
const RenderHTMLQuestion = (props) => (<p dangerouslySetInnerHTML={{__html:props.HTML}}></p>)
const RenderHTMLAnswer = (props) => (<li dangerouslySetInnerHTML={{__html:props.HTML}}></li>)
fetch('https://opentdb.com/api.php?amount=10&category=9&type=multiple')
.then(results => {
return results.json();
}).then(data => {
let questions = data.results.map((question, index) => {
return(
<div key={index} className="questionWrapper">
<div className="question" key={question.question}>
<RenderHTMLQuestion HTML={question.question} />
</div>
<ul className="answers">
<RenderHTMLAnswer key={question.correct_answer} HTML={question.correct_answer} />
{question.incorrect_answers.map((answer, index) => (
<RenderHTMLAnswer key={index} HTML={answer} />
))}
</ul>
</div>
)
})
this.setState({questions: questions});
})
}
render() {
return (
<div className="container2">
{this.state.questions}
</div>
)
}
}
export default QuestionContainer;
You can try this,
let questions = data.results.map((question, index) => {
let correctAnswer = false
return (
<div key={index} className="questionWrapper">
<div className="question" key={question.question}>
<RenderHTMLQuestion HTML={question.question} />
</div>
<ul className="answers">
{question.incorrect_answers.map((answer, index) => {
if(Math.floor(Math.random() * Math.floor(question.incorrect_answers.length-1)) === index && !correctAnswer) {
correctAnswer = true
return <> <RenderHTMLAnswer key={question.correct_answer} HTML={question.correct_answer} /> <RenderHTMLAnswer key={index} HTML={answer} /> </>
}
return <RenderHTMLAnswer key={index} HTML={answer} />
})}
</ul>
</div>
)
})
Math.random()
Note: Don't use componentWillMount (UNSAFE), instead you can go for componentDidMount for your API call.
Update
You can also try this,
let questions = data.results.map((question, index) => {
question.incorrect_answers.splice(Math.floor(Math.random() * Math.floor(question.incorrect_answers.length + 1)), 0, question.correct_answer)
return (
<div key={index} className="questionWrapper">
<div className="question" key={question.question}>
<RenderHTMLQuestion HTML={question.question} />
</div>
<ul className="answers">
{question.incorrect_answers.map((answer, index) => {
return <RenderHTMLAnswer key={index} HTML={answer} />
})}
</ul>
</div>
)
})
Array.prototype.splice()
Demo
You can try using lodash
https://lodash.com/docs/4.17.15#shuffle
const answers = _.shuffle(_.concat(correct_answers, incorrect_answers));
return (
<ul className="answers">
{answers.map((answer, index) => (<RenderHTMLAnswer key={index} HTML={answer} />))}
</ul>
)

React material-ui, grid list cannot render grid items

export interface FlatsGridProps {
flats: IClusterFlats[];
}
export const FlatsGrid: React.StatelessComponent<FlatsGridProps> = (props: FlatsGridProps) => {
if (props.flats.length === 0) {
return (<div> empty </div>);
}
return (
<div style={styles.root}>
<GridList
cols={2}
style={styles.gridList}
cellHeight={180}>
{props.flats.map((f, i) => {
<div key={i}> element </div>
})}
</GridList>
</div>
)
};
When I render GridList control it throws exception
Cannot read property 'props' of null in GridList.js
View from console below.
I think you need to return the child from the map. The grid.js complains about its child being undefined.
<GridList
cols={2}
style={styles.gridList}
cellHeight={180}>
{props.flats.map((f, i) => {
return (<div key={i}> element </div>)
})}
</GridList>

Resources