How to pass props onClicked function in one component to another - javascript

I want to set that when I click onClick in the component Albums.js, I get the contents of the component photos.js. Specifically, choosing one album will trigger a photo array from this album using the Photos.js component.
Albums.js
import React from 'react';
import PropTypes from 'prop-types';
import './forAlbum.css'
class Albums extends React.Component {
constructor(props) {
super(props);
this.state = {
};
this.onClicks = this.onClicks.bind(this);
}
onClicks = () => {
console.log("lamus");
}
render () {
const albums = this.props.albums.map((album, i) => {
return (
<AlbumThumb name={album.name} img={album.photos[0].src} id={i} key={i} />
);
});
// console.log(albums);
console.log(this.onClicks);
return (
<div className="thumbContainer">{albums}</div>
)
}
}
const AlbumThumb = (props) => (
<div className="thumb">
<div className="thumbImgWrap">
<img src={require('./GalleryImages/' + props.img)} alt={props.name} />
</div>
<h3 className="thumbTitle">{props.name}</h3>
</div>
);
export default Albums;
This is Photos.js
import React from 'react';
import './forAlbum.css'
const Photos = (props) => {
const cliced = () => {
console.log("cliced");
}
const photos = props.photos.map(({ photos }) =>
<div>
<ul key={photos.id}>
{photos.id}
</ul>
{photos.map((eachThing, i) => {
return (
<PhotoMain name={eachThing.cap} img={eachThing.src} id={i} key={i} />
);
})}
</div>
);
// console.log(photos);
return <div className="thumbContainer" onClick={props.clicked}>{photos}</div>;
};
const PhotoMain = (props) => (
<div className="thumb" >
<div className="thumbImgWrap">
<img src={require('./GalleryImages/' + props.img)} alt={props.name} />
</div>
<h3 className="thumbTitle">{props.name}</h3>
</div>
);
export default Photos;
But i don't how to connect this, Maybe someone has an idea or suggestion?
I try to invoke this in next component GalleryPhotos.js
<Albums albums={data3} onClick={this.onClicks} />
<Photos photos={data3} />
const data3 = [
{
id: '1',
name: "Siatkówka",
photos: [
{
id: "11",
src: "siatkowka1.jpg",
cap: "Siatkówka"
},
{
id: "12",
src: "siatkowka2.jpg",
cap: "Siatkówka2"
},
{
id: "13",
src: "siatkowka3.jpg",
cap: "Siatkówka3"
}
]
},
{
id: '2',
name: "Piłka nożna",
photos:[
{
id: "21",
src: "pilkaNozna1.jpg",
cap: "Piłka nożna1"
},
{
id: "22",
src: "pilkaNozna2.jpeg",
cap: "Piłka nożna2"
},
{
id: "23",
src: "pilkaNozna3.jpg",
cap: "Piłka nożna3"
}
]
}
];
EDIT 1 - filter method
const Photos = (props) => {
const photos = props.photos.filter(photos => photos.id === '1').map(({photos}) =>
<div>
<ul key={photos.id}>
{photos.id}
</ul>
{photos.filter(eachThing => eachThing.id === eachThing.id).map((eachThing, i) =>
{
return (
<PhotoMain name={eachThing.cap} img={eachThing.src} id={i} key={i} />
);
})}
</div>
);
console.log(photos);
return <div className="thumbContainer">{photos}</div>;
};
I used the Filter.js method and thanks to it I am able to select by identifying the specific album ID, which photos must appear, but I am wondering how to set it as dynamic relative to the component. The biggest difficulty is understanding how the connections between components are made to make this filter effective. In addition, I wonder if the component Albums.js while filtering has something to logic
Edit 2:
Console message:
Uncaught TypeError: props.photos.filter is not a function
at Photos (Photos.js:6)
at mountIndeterminateComponent (react-dom.development.js:14592)
at beginWork (react-dom.development.js:15082)
at performUnitOfWork (react-dom.development.js:17903)
at workLoop (react-dom.development.js:17944)
at HTMLUnknownElement.callCallback (react-dom.development.js:147)
at Object.invokeGuardedCallbackDev (react-dom.development.js:196)
at invokeGuardedCallback (react-dom.development.js:250)
at replayUnitOfWork (react-dom.development.js:17224)
at renderRoot (react-dom.development.js:18037)
at performWorkOnRoot (react-dom.development.js:18919)
at performWork (react-dom.development.js:18826)
at performSyncWork (react-dom.development.js:18799)
at interactiveUpdates$1 (react-dom.development.js:19109)
at interactiveUpdates (react-dom.development.js:2328)
at dispatchInteractiveEvent (react-dom.development.js:5134)
The above error occurred in the <Photos> component:
in Photos (at GalleryPhotosVideos.js:208)
in div (at GalleryPhotosVideos.js:204)
in div (at GalleryPhotosVideos.js:196)
in CSSTransitionGroupChild (created by TransitionGroup)
in span (created by TransitionGroup)
in TransitionGroup (created by CSSTransitionGroup)
in CSSTransitionGroup (at GalleryPhotosVideos.js:190)
in GalleryPhotosVideos (created by Route)
in Route (at Content.js:33)
in Switch (at Content.js:24)
in div (at Content.js:23)
in Content (at App.js:14)
in div (at App.js:12)
in App (at src/index.js:10)
in Router (created by BrowserRouter)
in BrowserRouter (at src/index.js:10)
Uncaught TypeError: props.photos.filter is not a function
at Photos (Photos.js:6)
at mountIndeterminateComponent (react-dom.development.js:14592)
at beginWork (react-dom.development.js:15082)
at performUnitOfWork (react-dom.development.js:17903)
at workLoop (react-dom.development.js:17944)
at renderRoot (react-dom.development.js:18022)
at performWorkOnRoot (react-dom.development.js:18919)
at performWork (react-dom.development.js:18826)
at performSyncWork (react-dom.development.js:18799)
at interactiveUpdates$1 (react-dom.development.js:19109)
at interactiveUpdates (react-dom.development.js:2328)
at dispatchInteractiveEvent (react-dom.development.js:5134)
This message I see when I click
TypeError: props.photos.filter is not a function
const photos = props.photos.filter(photos => photos.id === photos.id).map(({photos}) =>

The component containing your Album and Photos should only render the Photos component if a state boolean value is true. When click on your album, this value will be updated :
import React, { Component } from 'react'
class ThankYou extends Component {
constructor(props) {
super(props)
this.state = {
showPhotos: false
}
}
albumClicked = ev => {
this.setState({ showPhotos: true })
}
render() {
return (
<>
<Albums albums={data3} onClick={this.albumClicked} />
{this.state.showPhotos && <Photos photos={data3} />}
</>
)
}
}
And then call the function passed in the album :
const AlbumThumb = (props) => (
<div className="thumb" onClick={ev => {props.onClick()}}> //Calls the parent function when clicked
<div className="thumbImgWrap">
<img src={require('./GalleryImages/' + props.img)} alt={props.name} />
</div>
<h3 className="thumbTitle">{props.name}</h3>
</div>
);
EDIT :
I did not notice that AlbumThumb wasn't your component. You will have to move the function up to the Album render function (and remove it from AlbumThumb) :
render() {
const albums = this.props.albums.map((album, i) => {
return (
<div onClick={ev => { props.onClick() }}>
<AlbumThumb name={album.name} img={album.photos[0].src} id={i} key={i} />
</div>
);
});
// console.log(albums);
console.log(this.onClicks);
return (
<div className="thumbContainer">{albums}</div>
)
}
EDIT 2 :
Filtering albums by owners name :
this.props.albums.filter(album => album.name.includes(myFilterString)).map(...
EDIT 3 :
Your parent class will have to be aware of wich album got selected, you will have to send your album data back to it using the onClick function :
const albums = this.props.albums.map((album, i) => {
return (
<div onClick={props.onClick(album)}>
<AlbumThumb name={album.name} img={album.photos[0].src} id={i} key={i} />
</div>
);
});
You can then tweak your parent class to store the whole selected album, and display photos depending on it :
class ThankYou extends Component {
constructor(props) {
super(props)
this.state = {
selectedAlbum: null
}
}
albumClicked = selectedAlbum => ev => {
this.setState({ selectedAlbum })
}
render() {
const { selectedAlbum } = this.state
return (
<>
<Albums albums={data3} onClick={this.albumClicked} />
{selectedAlbum && <Photos photos={data3.find(album => album.name === selectedAlbum.name)} />}
</>
)
}
}

Manage your data as state in an outer component as state. And pass it as props to both Album and Photos. Update photos from album component.
class Outer extends Component {
constructor(props) {
super(props)
this.state = {
photos: [],
albums: [
{
id: '1',
name: "Siatkówka",
photos: [
{
id: "11",
src: "siatkowka1.jpg",
cap: "Siatkówka"
},
{
id: "12",
src: "siatkowka2.jpg",
cap: "Siatkówka2"
},
{
id: "13",
src: "siatkowka3.jpg",
cap: "Siatkówka3"
}
]
},
{
id: '2',
name: "Piłka nożna",
photos:[
{
id: "21",
src: "pilkaNozna1.jpg",
cap: "Piłka nożna1"
},
{
id: "22",
src: "pilkaNozna2.jpeg",
cap: "Piłka nożna2"
},
{
id: "23",
src: "pilkaNozna3.jpg",
cap: "Piłka nożna3"
}
]
}
]
}
}
const updatePhotos = (albumId) => {
// pass this function to album as prop and bind with each album.
const album = this.state.albums.filter((album) => album.id === albumId)
this.setState({
photos: album.photos
})
}
render() {
<>
<Albums albums={this.state.albums} clickHandler={this.updatePhotos} />
{this.state.photos ? <Photos photos={this.state.photos} /> : null}
</>
}
}
In Albums, call clickHandler with album id
<AlbumThumb name={album.name}
img={album.photos[0].src}
id={i}
key={i}
onClick={() => this.props.clickHandler(album.id)} />

Related

Access data from two parent containers - ReactJS

I have an component that renders different types of fields called Item. Item may render a select box with a list of Users or a list of Inventory. I have two containers: one for Users and another for Inventory. I originally thought to nest my containers but that appears to freeze my react app. Inventories and Users containers are identical except that one container holds inventory items and the other holds users.
Here is the Users container:
import React, { Component } from 'react';
class UsersContainer extends Component{
constructor(props){
super(props);
this.state = {
users: []
}
}
componentDidMount(){
//put api call here
this.setState({users: [{id: 1, name: "Test Name", email: "test#yahoo.com"}, {id: 2, name: "John Doe", email: "johndoe#gmail.com"}, {id: 3, name: "Jane Doe", email: "janedoe#yahoo.com"}]})
}
render(){
return(
<div className="users-container">
{React.Children.map(this.props.children, child => (
React.cloneElement(child, {...this.props, users: this.state.users })
))}
</div>
)
}
}
export default UsersContainer;
I originally tried to nest the containers but this causes React to freeze:
<UsersContainer>
<InventoriesContainer>
{this.props.items.map(i => (
<Item name={i.name} />
))}
</InventoriesContainer>
</UsersContainer>
Item looks something like this:
function elementUsesInvetory(inventories){
//returns selectbox with list of inventory
}
function elementUsesUsers(users){
//returns selectbox with list of users
}
function Item(props){
render(){
return(
<>
{elementUsesUsers(props.inventories)}
{elementUsesInventory(props.users)}
</>
);
}
}
How can I provide the data from UsersContainer and InventoriesContainer to the Item component?
Merging them into one component would avoid a lot of confusion. If you still want to nest them, you might want to pass the props by prop-drilling or by using the context API. React.cloneElement isn't preferred for nested child components. More on that here
You can pass down the data with the help of React's context API. The UsersContainer component holds the Provider and passes users down to Inventories
The Inventories will then pass on the users and inventories as props to the Items component. I'm not sure if you need separate functions for the select boxes but I've added them in the demo anyway.
const MyContext = React.createContext();
class UsersContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
users: []
};
}
componentDidMount() {
//put api call here
this.setState({
users: [
{ id: 1, name: "Test Name", email: "test#yahoo.com" },
{ id: 2, name: "John Doe", email: "johndoe#gmail.com" },
{ id: 3, name: "Jane Doe", email: "janedoe#yahoo.com" }
]
});
}
render() {
return (
<div className="users-container">
<MyContext.Provider value={this.state.users}>
{this.props.children}
</MyContext.Provider>
</div>
);
}
}
class Inventories extends React.Component {
static contextType = MyContext;
constructor(props) {
super(props);
this.state = {
inventories: []
};
}
componentDidMount() {
//put api call here
this.setState({
inventories: [
{ id: 1, name: "Test Name", email: "test#yahoo.com" },
{ id: 2, name: "John Doe", email: "johndoe#gmail.com" },
{ id: 3, name: "Jane Doe", email: "janedoe#yahoo.com" }
]
});
}
render() {
return (
<div className="inventory-container">
{React.Children.map(this.props.children, (child) => {
return React.cloneElement(child, {
...this.props,
users: this.context,
inventories: this.state.inventories
});
})}
</div>
);
}
}
function Items(props) {
function usersSelect(items) {
return (
<select>
{items.map((item) => (
<option key={"user"+item.id} value="{item.id}">
{item.name}
</option>
))}
</select>
);
}
function inventoriesSelect(items) {
return (
<select>
{items.map((item) => (
<option key={item.id} value="{item.id}">
{item.name}
</option>
))}
</select>
);
}
return (
<div>
<h2>users</h2>
{usersSelect(props.users)}
<h2>inventories</h2>
{inventoriesSelect(props.inventories)}
</div>
);
}
function App() {
return (
<div>
<UsersContainer>
<Inventories>
<Items />
</Inventories>
</UsersContainer>
</div>
);
}
ReactDOM.render(<App/>, document.getElementById("root"))
<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"></div>
I good approach would be to put the state in common between those components in a level up in the tree component.
So what are you trying to do:
<UsersContainer>
<InventoriesContainer>
{this.props.items.map(i => (
<Item name={i.name} />
))}
</InventoriesContainer>
</UsersContainer>
Would be:
RealFatherComponent extends Component {
// state that Item will need will be set here
render() {
return (
< UsersContainer **propsShared** >
<Item **propsShared** />
</UsersContainer>
< InventoriesContainer **propsShared** >
<Item **propsShared** /> );
</InventoriesContainer>
}
}

Need to Trigger individual Element in an array of elements written through map function reactjs

class Services extends Component {
constructor(props) {
super(props);
this.state = {showoffer: false};
}
showOffers=( )=>{
this.setState({showoffer: !this.state.showoffer});
}
render() {
return (
<div className="OSServicesContainer">
<img className="OSlogomark" src={logomark} alt="logo mark" />
<article className="OssHeadingText">OOM INTERIORS OFFERS</article>
{offersdata.map((offers,index)=>{
return ( <div key={index} className="OssoffersContainermain">
<div className="OssoffersContainer">
<div className="OssofferHeadingmain">
<article className="OssofferHeading">{offers.heading}</article>
</div>
<article className="OssofferText">{offers.subheading}</article>
<div className="OssofferViewbtnmain">
<article key={index} className="OssofferViewbtn" onClick={this.showOffers}>{this.state.showoffer?"View Less":"View More"}</article>
</div>
</div>
{!this.state.showoffer?
null:
<div className="OssOfferSubCompmain">
{offers.offersub.map((offer,key) =>{
return <OssOfferSubComp ofrtext={offer.text} ofrsubtext={offer.subtext} />
})}
</div>}
</div>
)
})}
</div>);
}
}
export default Services;
Above is my code
i want to call showoffer function and update only that element clicked
please what shall i do it is triggering all elements
how to trigger single element??
You can try something like this:
`class Services extends Component {
constructor(props) {
super(props);
this.state = {showoffer: 0};
}
showOffers = ( offerIndex ) => {
this.setState({showoffer: offerIndex});
}
hideOffers = () => {
this.setState({showoffer: 0});
}
render() => {
...
<div className="OssofferViewbtnmain">
<article key={index} onClick={ () => this.showOffers(index) }>
{this.state.showoffer?"View Less":"View More"}
</article>
</div>
...
{
this.state.showOffer && this.state.showOffer === index
? // then show it
: ''
}
}`
Hey if you wish to have multiple items open at the same time you can do something like this where you mutate the mapped item to track show hide state. I have added a visible property to the list item that keeps track if the item is open or closed:
import React, { Component } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends Component {
state = {
items: [
{ header: "Test 1", extra: "Some extra content A" },
{ header: "Test 2", extra: "Some extra content B" },
{ header: "Test 3", extra: "Some extra content C" }
]
};
onItemClick(index) {
const selected = this.state.items[index];
this.setState({
items: [
...this.state.items.slice(0, index),
{ ...selected, visible: !selected.visible },
...this.state.items.slice(index + 1)
]
});
}
render() {
return (
<div>
<ul>
{this.state.items.map((item, index) => {
return (
<li
key={index}
style={{ cursor: "pointer" }}
onClick={() => this.onItemClick(index)}
>
<h3>{item.header}</h3>
{item.visible ? <div>{item.extra}</div> : null}
</li>
);
})}
</ul>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
https://codesandbox.io/s/busy-germain-hdmrn

Using JsonSchemaForm on change to update field's content

I am trying to use JsonSchema-Form component but i ran into a problem while trying to create a form that, after choosing one of the options in the first dropdown a secondary dropdown should appear and give him the user a different set o options to choose depending on what he chose in the first dropdown trough an API call.
The thing is, after reading the documentation and some examples found here and here respectively i still don't know exactly how reference whatever i chose in the first option to affect the second dropdown. Here is an example of what i have right now:
Jsons information that are supposed to be shown in the first and second dropdowns trough api calls:
Groups: [
{id: 1,
name: Group1}
{id: 2,
name: Group2}
]
User: [User1.1,User1.2,User2.1,User2.2,User3.1,User3.2, ....]
If the user selects group one then i must use the following api call to get the user types, which gets me the the USER json.
Component That calls JSonChemaForm
render(){
return(
<JsonSchemaForm
schema={someSchema(GroupOptions)}
formData={this.state.formData}
onChange={{}}
uiSchema={someUiSchema()}
onError={() => {}}
showErrorList={false}
noHtml5Validate
liveValidate
>
)
}
SchemaFile content:
export const someSchema = GroupOptions => ({
type: 'object',
required: [
'groups', 'users',
],
properties: {
groups: {
title: 'Group',
enum: GroupOptions.map(i=> i.id),
enumNames: GroupOptions.map(n => n.name),
},
users: {
title: 'Type',
enum: [],
enumNames: [],
},
},
});
export const someUISchema = () => ({
groups: {
'ui:autofocus': true,
'ui:options': {
size: {
lg: 15,
},
},
},
types: {
'ui:options': {
size: {
lg: 15,
},
},
},
});
I am not really sure how to proceed with this and hwo to use the Onchange method to do what i want.
I find a solution for your problem.There is a similar demo that can solve it in react-jsonschema-form-layout.
1. define the LayoutField,this is part of the demo in react-jsonschema-form-layout.To make it easier for you,I post the code here.
Create the layoutField.js.:
import React from 'react'
import ObjectField from 'react-jsonschema-form/lib/components/fields/ObjectField'
import { retrieveSchema } from 'react-jsonschema-form/lib/utils'
import { Col } from 'react-bootstrap'
export default class GridField extends ObjectField {
state = { firstName: 'hasldf' }
render() {
const {
uiSchema,
errorSchema,
idSchema,
required,
disabled,
readonly,
onBlur,
formData
} = this.props
const { definitions, fields, formContext } = this.props.registry
const { SchemaField, TitleField, DescriptionField } = fields
const schema = retrieveSchema(this.props.schema, definitions)
const title = (schema.title === undefined) ? '' : schema.title
const layout = uiSchema['ui:layout']
return (
<fieldset>
{title ? <TitleField
id={`${idSchema.$id}__title`}
title={title}
required={required}
formContext={formContext}/> : null}
{schema.description ?
<DescriptionField
id={`${idSchema.$id}__description`}
description={schema.description}
formContext={formContext}/> : null}
{
layout.map((row, index) => {
return (
<div className="row" key={index}>
{
Object.keys(row).map((name, index) => {
const { doShow, ...rowProps } = row[name]
let style = {}
if (doShow && !doShow({ formData })) {
style = { display: 'none' }
}
if (schema.properties[name]) {
return (
<Col {...rowProps} key={index} style={style}>
<SchemaField
name={name}
required={this.isRequired(name)}
schema={schema.properties[name]}
uiSchema={uiSchema[name]}
errorSchema={errorSchema[name]}
idSchema={idSchema[name]}
formData={formData[name]}
onChange={this.onPropertyChange(name)}
onBlur={onBlur}
registry={this.props.registry}
disabled={disabled}
readonly={readonly}/>
</Col>
)
} else {
const { render, ...rowProps } = row[name]
let UIComponent = () => null
if (render) {
UIComponent = render
}
return (
<Col {...rowProps} key={index} style={style}>
<UIComponent
name={name}
formData={formData}
errorSchema={errorSchema}
uiSchema={uiSchema}
schema={schema}
registry={this.props.registry}
/>
</Col>
)
}
})
}
</div>
)
})
}</fieldset>
)
}
}
in the file, you can define doShow property to define whether to show another component.
Next.Define the isFilled function in JsonChemaForm
const isFilled = (fieldName) => ({ formData }) => (formData[fieldName] && formData[fieldName].length) ? true : false
Third,after you choose the first dropdown ,the second dropdown will show up
import LayoutField from './layoutField.js'
const fields={
layout: LayoutField
}
const uiSchema={
"ui:field": 'layout',
'ui:layout': [
{
groups: {
'ui:autofocus': true,
'ui:options': {
size: {
lg: 15,
},
},
}
},
{
users: {
'ui:options': {
size: {
lg: 15,
},
},
doShow: isFilled('groups')
}
}
]
}
...
render() {
return (
<div>
<Form
schema={schema}
uiSchema={uiSchema}
fields={fields}
/>
</div>
)
}

ReactJS: how to map JSON elements sequentially and show the hidden div on click

I'm trying to load items from JSON and toggle a dropdown div with description on click. While I can display elements sequentially (ex: loc1 & desc1, loc2 & desc2) on static divs I'm having trouble finding out how to render it properly when the second part (desc) is hidden and only shows when the loc div is clicked.
What would be the best way to map the result so it doesn't show as loc1 & loc2, desc1 & desc2 but as loc1 & desc1, loc2 & desc2?
Code:
var places = {
library: {
location: [
{
loc_name: "library1",
"desc": "desc1 : Modern and spacious building"
},
{
loc_name: "library2",
"desc": "desc2 : A cosy small building"
}
]
}
};
function contentClass(isShow) {
if (isShow) {
return "content";
}
return "content invisible";
}
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = { isShow: false };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(function (prevState) {
return { isShow: !prevState.isShow };
});
}
render() {
const libraries_desc = places.library.location.map((libr) =>
<div>
<p>{libr.desc}</p>
</div>
);
const lib_names = places.library.location.map((libr) =>
<div>
<p>{libr.loc_name}</p>
</div>
);
return (
<div>
<div className='control' onClick={this.handleClick}>
<h4>{lib_names}</h4>
<div className={contentClass(this.state.isShow)}>{libraries_desc}</div>
</div>
</div>
);
}
}
render((
<Toggle />
), document.getElementById('root'));
Current result:
library1
library2
desc1 : Modern and spacious building
desc 2 : A cosy small building
Desired Result:
library1
desc1 : Modern and spacious building (hidden but shown when clicked)
library2
desc 2 : A cosy small building (hidden but shown when clicked)
Codesandbox
I might try extracting a location into a separate component. By extracting it, each location is responsible for knowing its state. In your case, that means its visibility (controlled by this.state.isShow).
Here's how you could do it:
import React from 'react';
import { render } from 'react-dom';
var places = {
library: {
location: [
{
loc_name: "library1",
"desc": "Modern and spacious building"
},
{
loc_name: "library2",
"desc": "A cosy small building"
}
]
}
};
class Location extends React.Component {
constructor(props) {
super(props);
this.state = { isShow: false };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(function (prevState) {
return { isShow: !prevState.isShow };
});
}
contentClass(isShow) {
if (isShow) {
return "content";
}
return "content invisible";
}
render() {
return (
<div className='control' onClick={this.handleClick}>
<h4>{this.props.desc}</h4>
<div className={this.contentClass(this.state.isShow)}>{this.props.loc_name}</div>
</div>
)
}
}
class Toggle extends React.Component {
constructor(props) {
super(props);
}
render() {
const locations = places.library.location.map(location => {
return <Location {...location} />
})
return (
<div>
{locations}
</div>
);
}
}
render((
<Toggle />
), document.getElementById('root'));
Your Toggle Component should be like this.
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {
isShow: false,
id: -1, // initial value
};
}
handleClick = (id) => {
this.setState({
isShow: !this.state.isShow,
id: id
});
}
render() {
const { location } = places.library;
const { isShow, id } = this.state;
return (
<div className="control">
{location.map((libr, index) => (
<div key={index} onClick={() => { this.handleClick(index) }}>
<p>{libr.loc_name}</p>
{(isShow && (id === index)) && <p>{libr.desc}</p>}
</div>
))}
</div>
);
}
}
So when you click on the div element. A click event will be triggered called handleClick which will pass the index as a param to the function. which will set isShow to false or truth and vice versa along with the current element you want to show which will be selected through this.state.id. So everytime isShow is true and this.state.id matched index element of the array. Your description will show otherwise it will be hidden as you want.
So your desired result will be something like this.
library1
desc1 : Modern and spacious building (hidden but shown when clicked)
library2
desc 2 : A cosy small building (hidden but shown when clicked)

Unable to pass props to child component in react

I'm still getting to grips with react but I can't see why this isn't working, it should be passing the props from tabs into <Tab /> and outputting the button each time.
If I put no text next to {this.props.content} it doesn't display anything, if I put testText next to {this.props.content} it will output the button 5 times but only display testText not the name field it should be displaying via the content={item.name} prop
class TopCategories extends React.Component {
render() {
const Tab = () => (
<TestBtn key={this.props.key} >
testText {this.props.content}
</TestBtn>
)
const items = [
{ id: 1, name: 'tab-1', text: 'text' },
{ id: 2, name: 'tab-2', text: 'text' },
{ id: 3, name: 'tab-3', text: 'text' },
{ id: 4, name: 'tab-4', text: 'text' },
{ id: 5, name: 'tab-5', text: 'text' },
]
const tabs = items.map(item =>
<Tab key={item.id} content={item.name} />,
)
return (
<Container>
<Wrapper>
{tabs}
</Wrapper>
</Container>
)
}
}
export default TopCategories
You need to pass props to the stateless function and since it's a stateless component, this is not available. It should be something like:
const Tab = (props) => {
return (
<TestBtn key={props.key} >
testText {props.content}
</TestBtn>
);
}

Categories