How to fix: How to show state with onClick to div?(React) - reactjs

I have sidebar with document types on it(docs, table, slider, html ..). I want that, if i click on docs element it will show docs in another div like a header.
I have 3 files: DocumentType.tsx, Sidebar.tsx and Results.tsx
In DocumentType.tsx:
import React from 'react';
const documentType = (props ) =>{
return(
<div>
<p id="fileType">{props.type}</p>
</div>
)
};
export default documentType;
In Sidebar.tsx:
typeState = {
documentTypes: [
{ type: "Dokumendid" },
{ type: "PDF" },
]
}
toDocument = () => {
this.setState({
documentTypes: [
{ type: "Dokumendid" }
console.log("Document was clicked");
]
})
}
toPdf = () => {
this.setState({
documentTypes: [
{ type: "Pdf" }
console.log("PDF was clicked")
]
})
}
render(){
return(
<a className="a" href="/search?filter%3Atype=doc" onClick={this.toDocument}>
<div className="icons dokument">
<img src={dokument} alt="dokument"/>
<a className="title">dokument</a>
</div>
</a>
<a className="a" href="/search?filter%3Atype=pdf" onClick={this.toPdf}>
<div className="icons pdf">
<img src={pdf} alt="pdf"/>
<a className="title">pdf</a>
</div>
</a>
)
}
And in Results.tsx:
...
<DocumentType />
..

You want to show a document type in Results component when a document in Sidebar component is clicked.
You have documentType state in Sidebar component and you want to pass it to Results component. So for that you can make Results component as child component of Sidebar component and pass the selected document type i.e documentType state as props.
Sidebar.js
import React, {Component} from 'react'
import Results from 'path-to-results';
class Sidebar extends Component {
state = {
// instead of using "documentType" as array
// you can make it null for initial value
documentType: null
}
// instead of using "toPDF" or "toDocument" method
// you can use single method to update the state
handleDocType = (docType) => {
this.setState({
documentType: docType
})
}
render() {
return (
<div>
// pass "document" as argument to handleDocType method
<a className="a" href="#" onClick={() => this.handleDocType('document')}>
<div className="icons dokument" >
<img src="" alt="dokument"/>
<a className="title">dokument</a>
</div>
</a>
// pass "pdf" as argument to handleDocType method
<a className="a" href="#" onClick={() => this.handleDocType('pdf')}>
<div className="icons pdf">
<img src="" alt="pdf"/>
<a className="title">pdf</a>
</div>
</a>
// checking if "documentType" is null or not
// if it is null nothing is rendered
// if it is not null then "Results" component is rendered
{ this.state.documentType && <Results type={this.state.documentType} /> }
</div>
)
}
}
Results.js
import React, { Component } from 'react'
import DocType from 'path-to-doctype'
class Results extends Component {
// .... your other codes
render() {
return (
<div>
// ....... your other codes
<DocType type={this.props.type} />
</div>
)
}
}
export default Results
DocType.js
import React from 'react';
const DocumentType = (props ) =>{
return(
<div>
<p id="fileType">{props.type}</p>
</div>
)
};
export default DocumentType;
UPDATE
If Sidebar and DocType components are children components of Results component then add documentType state to Results component and pass documentType state as props to DocType component.
Results.js
class Results extends Component {
// add state "documentType"
state = {
documentType: null
}
// add "handleDocType" method
handleDocType = (docType) => {
this.setState({
documentType: docType
})
}
// .... your other codes
render() {
return (
<div>
// .... your other codes
// pass "handleDocType" as props to Sidebar component
<Sidebar handleDocType={this.handleDocType}/>
// pass "documentType" state as props to DocType component
<DocType type={this.state.documentType} />
</div>
)
}
}
export default Results
Sidebar.js
class Sidebar extends Component {
// use "docTypeHandler" to call parent "handleDocType" method
// that updates "documentType" state in Results component
docTypeHandler = (doctype) => {
this.props.handleDocType(doctype)
}
render() {
return (
<div>
<a className="a" href="#" onClick={() => this.docTypeHandler('document')}>
<div className="icons dokument" >
<img src="" alt="dokument"/>
<a className="title">dokument</a>
</div>
</a>
<a className="a" href="#" onClick={() => this.docTypeHandler('pdf')}>
<div className="icons pdf">
<img src="" alt="pdf"/>
<a className="title">pdf</a>
</div>
</a>
</div>
)
}
}
export default Sidebar
DocType.js
const DocType = (props ) =>{
return(
<div>
<p id="fileType">{props.type}</p>
</div>
)
};

If I understood your question correctly.. you wanted to show data in a div when onClick event triggers..
lets say your state object has
state = {
data: ''
}
//clicked function
clicked =() => {
this.setState({data: 'clickedme'})
}
div element: <div onClick={this.clicked} >{this.state.data}</div>
simple example when an onClick event occurs a div and displaying the state data object..

Related

react-redux: Rendering a component after an API call

I am building an app which uses user input and shows number of recipes and they can click on recipe card to view ingredients as well. Every time they click on recipe card I make an API call to get appropriate recipe ingredient. But I am not able to figure out how to show the component which contains the recipe ingredients. I tried with conditional routing and conditional rendering as well but couldn't find the solution.
Recipe_Template.js
export class RecipeTemplate extends Component {
renderRecipe = recipeData => {
return recipeData.recipes.map(recipeName => {
return (
<div className="container">
<div className="row">
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<a
href={recipeName.source_url}
target="_blank"
onClick={() => {
this.props.fetchRecipeId(recipeName.recipe_id);
}}
>
<img
src={recipeName.image_url}
className="mx-auto d-block img-fluid img-thumbnail"
alt={recipeName.title}
/>
<span>
<h3>{recipeName.title}</h3>
</span>
</a>
<span}>
<h3>{recipeName.publisher}</h3>
</span>
</div>
</div>
</div>
);
});
};
render() {
return (
<React.Fragment>
{this.props.recipe.map(this.renderRecipe)}
</React.Fragment>
);
}
}
Recipe_Detail.js
class RecipeDetail extends Component {
renderRecipeDetail(recipeData) {
return recipeData.recipe.ingredients.map(recipeIngredient => {
return <li key={recipeIngredient}>recipeIngredient</li>;
});
}
render() {
if (this.props.recipeId === null) {
return <div>Loading...</div>;
}
return <ul>{this.props.recipeId.map(this.renderRecipeDetail)}</ul>;
}
}
function mapStateToProps({ recipeId }) {
return { recipeId };
}
export default connect(mapStateToProps)(RecipeDetail);
Not entirely sure why you would need Redux here (unless it's being shared among other nested components), but I'm fairly certain you can just utilize React state.
One approach would be to configure your routes as such:
<Route path="/recipes" component={Recipes} />
<Route path="/recipe/:id" component={ShowRecipe} />
When the user sends a query, gets some results, and you display all matching recipes to a Recipes component. Each recipe then has a name (and other associated displayable data) and a clickable link:
<Link to={`/recipe/id?recipeId=${recipeId}`}>View {recipeName} Recipe</Link>
which for simplicity sake might look like:
<ul>
<Link to="/recipe/id?recipeId=08861626">View Prosciutto Bruschetta Recipe</Link>
<Link to="/recipe/id?recipeId=04326743">View Pasta Bundt Loaf Recipe</Link>
...etc
</ul>
When the user clicks on the link, react-router sends the user to the ShowRecipe component with a unique recipeId.
ShowRecipe then makes another AJAX request to get the recipe details:
ShowRecipe.js
export default class ShowRecipe extends Component {
state = { recipeDetail: '' }
componentDidMount = () => {
const { recipeId } = this.props.location.query; // <== only natively available in react-router v3
fetch(`http://someAPI/recipe/id?recipeId=${recipeId}`)
.then(response => response.json())
.then(json => this.setState({ recipeDetail: json }));
}
render = () => (
!this.state.recipeDetails
? <div>Loading...</div>
: <ul>
{this.state.recipeDetail.map(ingredient => (
<li key={ingredient}>ingredient</li>
)}
</ul>
)
}
Another approach:
Have the recipeDetails stored and available within the original fetched recipes JSON. Then map over the recipes and create multiple <Card key={recipeId} recipeName={recipeName} recipeDetail={recipeDetail} /> components for each recipe.
which for simplicity sake might look like:
<div>
{this.state.recipes.map(({recipeId, recipeName, recipeDetail}), => (
<Card key={recipeId} recipeName={recipeName} recipeDetail={recipeDetail} />
)}
</div>
Then each individual Card has it's own state:
Card.js
export default class Card extends Component {
state = { showDetails: '' }
toggleShowDetails = () => this.setState(prevState => ({ showDetails: !this.state.showDetails }))
render = () => (
<div>
<h1>{this.props.recipeName} Recipe</h1>
<button onClick={toggleShowDetails}> {`${!this.state.showDetails ? "Show" : "Hide"} Recipe<button>
{ this.state.showDetails &&
<ul>
{this.props.recipeDetail.map(ingredient => (
<li key={ingredient}>ingredient</li>
)}
</ul>
}
)
}
Therefore, by default the recipeDetail is already there, but hidden. However, when a user clicks the Card's button, it will toggle the Card's showDetails state to true/false to display/hide the recipe detail.

Passing state back to child component

I'm trying to figure out how can i properly pass state back to the child component.
Currently I have list of items and everytime i click on one of the items it changes state of "selectedVideo" variable in parent component. And then I would like to add class to the item that corresponds to that state in that child component. Basically when I click on that item in that list it become highlighted because it just changed the state of parent component.
So the main parent component is here:
index.js
class App extends Component {
constructor(props) {
super(props)
this.state = {
videos2:[],
selectedVideo:null
}
this.DMSearch()
}
DMSearch(term){
fetch(`https://api.dailymotion.com/videos?fields=description,id,thumbnail_60_url,title,url,&limit=5&search=${term}`)
.then(result => result.json())
.then(videos2 => {
//console.log(videos2.list[0]);
this.setState({
videos2: videos2.list,
selectedVideo: videos2.list[0]
});
//console.log(this.state.selectedVideo);
});
}
render () {
const DMSearch = _.debounce((term) => { this.DMSearch(term)}, 400);
return (
<div>
<SearchBar onSearchTermChange= {DMSearch}/>
<VideoDetail video={this.state.selectedVideo}/>
<VideoList
onVideoSelect={selectedVideo=>this.setState({selectedVideo})}
videos2={this.state.videos2}/>
</div>
)
}
}
Now the child component which changes state onclick
video_list_item.js
const VideoListItem = ({video, onVideoSelect}) => {
const imageUrl = video.thumbnail_60_url;
return (
<li onClick={() => onVideoSelect(video)} className="list-group-item">
<div className="video-list media">
<div className="media-left">
<img className="media-obj" src={imageUrl}/>
</div>
<div className="media-body">
<div className="media-heading">{video.title}</div>
</div>
</div>
</li>
);
};
And what I want is to add class "active" to this specific line
<li onClick={() => onVideoSelect(video)} className="list-group-item">
Based on the state of selectedVideo that changed in index.js after clicking on that component.
Also here is the code for the whole list.
video_list.js
const VideoList = (props) => {
const videoItems = props.videos2.map((video)=>{
return (
<VideoListItem
onVideoSelect={props.onVideoSelect}
key={video.id}
video={video} />
)
})
return (
<ul className="col-md-4 list-group">
{videoItems}
</ul>
)
}
You have to pass the selectedVideo state of your App to the VideoList component,
<VideoList
videos2={this.state.videos2}
onVideoSelect={selectedVideo=>this.setState({selectedVideo})}
selectedVideo={this.state.selectedVideo}
/>
which in turn passes it to each VideoListItem
const videoItems = props.videos2.map((video)=>{
return (
<VideoListItem
onVideoSelect={props.onVideoSelect}
key={video.id}
video={video}
active={video === props.selectedVideo}
/>
)
})
so each item can compare itself to the selectedVideo and display an 'active' class if needed.

error while using state and map in reactjs

I am new to react. I am fetching github user info on search. I unable
to fetch data in my child component. this is my code below.
whats the problem , cant i use this.state.userList.map
class SearchHeader extends Component {
constructor(props) {
super(props);
this.state = {
errorMessage: '',
userList: [],
isOpen: false,
userName:''
};
this.toggle = this.toggle.bind(this);
this.getUsers = this.getUsers.bind(this);
}
toggle() {
this.setState({
isOpen: !this.state.isOpen
});
}
// componentWillMount(){
// this.getUsers();
// }
getUsers(e) {
console.log('get users called='+e.target.value);
fetch('https://api.github.com/search/users?q='+ e.target.value)
.then(res => res.json())
.then(
userList =>{
this.setState({userList: userList})
console.log(userList);
}
);
}
render() {
return (
<div>
<nav className="navbar navbar-expand-lg navbar-light bg-primary navbar-inner">
<div className="collapse navbar-collapse navbar-inner navb" >
<ul className="navbar-nav bg-light mr-auto">
<li className="nav-item dropdown">
<a className="nav-link dropdown-toggle auto" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Sort
</a>
<div className="dropdown-menu" aria-labelledby="navbarDropdown">
<a className="dropdown-item" href="#">Sort by Name (ascending)</a>
<a className="dropdown-item" href="#">Sort by Name (descending)</a>
<div className="dropdown-divider"></div>
<a className="dropdown-item" href="#">Sort by Rank (ascending)</a>
<a className="dropdown-item" href="#">Sort by Rank (descending)</a>
</div>
</li>
</ul>
<form className="form-inline my-2 my-lg-0 auto" onSubmit={this.getUsers}>
<div className="form-group">
<input className="form-control mr-sm-2" type="Search" placeholder="Search"
aria-label="Search"
id="userName"
onKeyUp={this.getUsers} >
</input>
</div>
</form>
</div>
</nav>
<div >
<UserList userList={this.state.userList}/>
</div>
</div>
);
}
}
export default SearchHeader;
This is my child component below where I am fetching data from parent
component
This is my child component below where I am fetching data from parent
component
class UserList extends Component {
constructor(props) {
super(props)
this.state ={
users:this.props.userList
}
}
render() {
return (
<div className="container-fluid">
<br />
{
this.state.users.map((user)=>
<div className="jumbotron container">
{user.login}
</div>
)
}
</div>
);
}
}
export default UserList;
You have several problems in your components:
do not copy parent's state into chilren states: users:this.props.userList. Use this.props directly instead and React will know it must re-render children
do not rely on current state to set new state. Use function with prevState instead of isOpen: !this.state.isOpen.
make a copy of event's value before passing it to setState like this const {value} = e.target;
assign unique key to each user in your list (not indexes!), or it won't re-render correctly on list update
So your code would look like this:
class SearchHeader extends Component {
constructor(props) {
super(props);
this.state = {
errorMessage: '',
userList: [],
isOpen: false,
userName:''
};
}
toggle = () => {
this.setState( (prevState) => ({
isOpen: !prevState.isOpen
}));
}
getUsers = (e) => {
const {value} = e.target;
console.log('get users called='+value);
fetch('https://api.github.com/search/users?q='+ value)
...
}
}
and:
class UserList extends Component {
// Use default constructor
render() {
const users = this.props.userList.map( (user) => (
<div className="jumbotron container" key={user.login}>
{user.login}
</div>
));
return (
<div className="container-fluid">
<br />
{users}
</div>
);
}
}
parent component change should be.
getUsers(e) {
console.log('get users called='+e.target.value);
fetch('https://api.github.com/search/users?q='+ e.target.value)
.then(res => res.json())
.then(
userList =>{
this.setState({userList: userList.items})
console.log(userList);
}
);
}
Change your user list and check initially values are there or not and you dont need to user state in userList component.
that is all because initially there are no values also there can be an case when you are setting state for userList value after fetching data that might be coming as null undefined or something else so put an console log there and check that too.
class UserList extends Component {
render() {
return (
{
this.props.userList && this.props.userList.length && this.props.userList.map((user)=>
{user.login}
)
}
</div>
);
}
}
export default UserList;

React recursively call method on children

I'm making a collapsible list with React. So far it works but now I want to implement a button that expands/collapses everything. Therefore the button need to adjust the state of all elements. I'm not sure what's the best way to tackle this problem though. This is what I have:
import React, {Component} from 'react';
class CollapsibleList extends Component {
constructor(props) {
super(props);
this.state = {
collapsed: true
};
this.subLists = [];
this.papers = [];
if (this.props.subtitles) {
for (let subList of this.props.subtitles) {
this.subLists.push(
<CollapsibleList level={this.props.level + 1} subtitles={subList.subtitles} title={subList.title}/>
);
}
}
this.toggleCollapse = this.toggleCollapse.bind(this);
this.expandAll = this.expandAll.bind(this);
this.collapseAll = this.collapseAll.bind(this);
}
expandAll() {
this.setState({collapsed: false});
this.subLists.forEach(subList => subList.expandAll());
}
collapseAll() {
this.setState({collapsed: true});
this.subLists.forEach(subList => subList.collapseAll());
}
toggleCollapse() {
this.setState(prevState => {
return {collapsed: !prevState.collapsed};
});
}
render() {
return (this.state.collapsed ?
<li className={'collapsibleListItem'}>
<div onClick={this.toggleCollapse}>
{this.props.title}
</div>
<img title={'Expand all'} className={'icon'} alt={'Expand all'} src={require('../expand_all.png')} onClick={this.expandAll}/>
<img title={'Collapse all'} className={'icon'} alt={'Collapse all'} src={require('../collapse_all.png')} onClick={this.collapseAll}/>
</li> :
<li className={'collapsibleListItem'}>
<div onClick={this.toggleCollapse}>
{this.props.title}
</div>
<img title={'Expand all'} className={'icon'} alt={'Expand all'} src={require('../expand_all.png')} onClick={this.expandAll}/>
<img title={'Collapse all'} className={'icon'} alt={'Collapse all'} src={require('../collapse_all.png')} onClick={this.collapseAll}/>
<ul className={'collapsibleList'}>
{this.subLists}
</ul>
</li>
);
}
}
export default CollapsibleList;
Unfortunately, that doesn't seem to work though.
I can't understand what you are trying to do in your code but you should have 2 different components; one for the list and one for the list item. It should be something like this:
// Parent component
import React from 'react';
import ListItem from './ListItem';
class List extends React.Component {
constructor() {
super();
this.state = {
collapsed: false
}
}
render() {
const data = ['abc', 'def', 'ghi']; // whatever you want to have
return(
<div>
<button onClick={() => this.setState({collapsed: !this.state.collapsed})}>
Collapse
</button>
<ul>
{
this.state.collapsed &&
data.map((val, key) => {
return(
<li>
<ListItem value={val} key={key} />
</li>
)
})
}
</ul>
</div>
)
}
}
And this is the child component
// child component
import React from 'react';
class ListItem extends React.Component {
constructor() {
super();
}
render() {
return(
<div>
{/*// render anything you want*/}
<p>{this.props.value}</p>
</div>
)
}
}
export default ListItem;
This code is just to give you an insight.

react change the class of list item on click

I have a react element like this:
import React, { PropTypes, Component } from 'react'
class AlbumList extends Component {
constructor(props) {
super(props);
this.state = {'active': false, 'class': 'album'};
}
handleClick() {
if(this.state.active){
this.setState({'active': false,'class': 'album'})
}else{
this.setState({'active': true,'class': 'active'})
}
}
render() {
var album_list
const {user} = this.props
if(user.data){
list = user.data.filter(album => album.photos).map((album => {
return <div className={"col-sm-3"} key={album.id}>
<div className={this.state.class} key={album.id} onClick={this.handleClick.bind(this)}>
<div className={"panel-heading"}>{ album.name }</div>
<div className={"panel-body"}>
<img className={"img-responsive"} src={album.photo.source} />
</div>
</div>
</div>
}))
}
return (
<div className={"container"}>
<div className="row">
{list}
</div>
</div>
)
}
}
export default AlbumList
Here map gives the list of filter data as I wanted. Here what I am doing changes the class of all the list element if I click on one.
I am getting the class name from this.state.class
How can I change the class of only element that i have clicked..
Thanks in advance ...
I have considered it once.So you have so many divs and you want to know which is clicked.My way to solve this problem is to give a param to the function handleClick and you can get the dom of the div while you click the div.Like this:
array.map(function(album,index){
return <div onClick={this.handleClick}/>
})
handleClick(e){
console.log(e.target);
e.target.className = 'active';
...
}
Then you have a param for this function.While you can use the e.target to get the dom of your div which is clicked.
There are some mistake into your code about the state.class.
class AlbumList extends Component {
constructor(props) {
super(props);
this.state = {'active': false, 'class': 'album'};
}
handleClick(e) {
if(e.target.class === 'active'){
e.target.className = 'album'
}else{
e.target.className = 'active'
}
}
render() {
var album_list
const {user} = this.props
if(user.data){
list = user.data.filter(album => album.photos).map((album => {
return (
<div className={"col-sm-3"} key={album.id}>
<div className='active' key={album.id} onClick={this.handleClick.bind(this)}>
<div className={"panel-heading"}>{ album.name }</div>
<div className={"panel-body"}>
<img className={"img-responsive"} src={album.photo.source} />
</div>
</div>
</div>
)
}))
}
return (
<div className={"container"}>
<div className="row">
{list}
</div>
</div>
)
}
}
You can try this and tell me anything wrong.

Resources