React router: Create 'Link to' paths from objects - javascript

I have an index of images and titles I am pulling in from the Dropbox api. These (when clicked) already create and mount new components. I'm trying to integrate react-router into this project but am struggling to generate Link to= paths using the array I have already created.
import { BrowserRouter, Route, Link } from 'react-router-dom'
//titles variable created via api
var titles=["title one","title two","title three",...]
class Index extends React.Component{
render(){
if(!this.props.imageSource.length)
return null;
let titles = this.props.imageTitles.map((el, i) => <p>{el}</p>)
let images = this.props.imageSource.map((el, i) =>
<div className="imageContainer">
<img key={i} className='indexImages' src={el} onClick = {this.props.indexTitleClick.bind(this,titles[i])}/>
<Link to={titles[i]} className="imageTitle" onClick = {this.props.indexTitleClick.bind(this,titles[i])}>{titles[i]}</Link>
</div>
)
return (
<div className="indexWrapper">
{images}
</div>
);
}
}
As you can see I'm trying to use the same method as I have for generating the imageTitles, but the href is simply "/" when rendered. Is it possible to dynamically generate paths in this way?
p.s. I've tried to keep the code concise here but happy to add in more info if needed.

Not sure you are aware of that but you overriding titles.
var titles=["title one","title two","title three",...] // from outside your component
let titles = this.props.imageTitles.map((el, i) => <p>{el}</p>) // inside render
Then, you are trying to link to the members of titles array but it holds members in this format:
<p>{el}</p>

First you have to create a Root class where you will declare some paths like the below example
<Router history={browserHistory} >
<Root path="/:pathVariable" component={ComponentClass}/>
</Router>
and then with browserHistory.push("/path") you will navigate to http://host:port/path

Related

Using Javascript to create html custom Tag

class Headers extends React.Component {
render() {
const selected = this.props.selectedPane;
const headers = this.props.panes.map((pane, index) => {
const title = pane.title;
const klass = index === selected ? 'active' : '';
return (
<li
key={index}
className={klass}
onClick={() => this.props.onTabChosen(index)}>
{title}{' '}
</li>
);
});
return (
<ul className='tab-header'>
{headers}
</ul>
);
}
}
export default class Tabs extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedPane: 0
};
this.selectTab = this.selectTab.bind(this);
}
selectTab(num) {
this.setState({selectedPane: num});
}
render() {
const pane = this.props.panes[this.state.selectedPane];
return (
<div>
<h1>Tabs</h1>
<div className='tabs'>
<Headers
selectedPane={this.state.selectedPane}
//onTabChosen={this.selectTab}
panes={this.props.panes}>
</Headers>
<div className='tab-content'>
<article>
hellooooo
{pane.content}
</article>
</div>
</div>
</div>
);
}
}
I'm currently creating a 3 tab section where if you click on a tab, it gives you a new pane.
When looking at the render function I see a custom tag called Headers.
I know it coming from the Headers class at the beginning, but how does that format work? Is that a custom tag we building?
Also when looking at its properties such as onTabChosen, when it is deleted in the render method (for learning purposes) and I click on a selected tab, an error comes up saying
"_this.props.onTabChosen is not a function".
this.props.onTabChosen(index).. was written in the Headers class but not as a function correct?
I guess because I am also confused on how this.props.onTabChosen(index) works since onTabChosen was never declared anywhere, just input after props.
When looking at the render function I see a custom tag called "Headers".
That is not a custom tag. That is a React Component.
I know it coming from the Headers class at the beginning, but how does that format work?
Headers is either a function or a class (i.e. a constructor function).
The function will be called and the first argument passed to it will be an object with properties and values that match the props on the JSX element.
If you're going to use React then read a tutorial, this is very introductory level stuff for the framework.
It is covered very early on in both the MDN tutorial and the official React tutorial.
I guess because I am also confused on how this.props.onTabChosen(index) works since onTabChosen was never declared anywhere, just input after props.
It was declared, just not in the piece of code you shared.

Render before or after child element

How do I render before or after a child element in a container?
I am learning React by integrating it into my own website. I started with this:
function createErrorSection(name, title, description) {
const section = document.createElement('section');
const container = document.createElement('div');
const h2 = document.createElement('h2');
const p = document.createElement('p');
section.appendChild(container);
container.appendChild(h2);
container.appendChild(p);
section.id = name;
section.classList = 'section-error';
container.classList = 'section-error-container';
h2.textContent = title;
p.textContent = description;
return section;
}
Which I turned into this:
function createErrorSection(name, title, description) {
return (
<section id={name} className='section-error'>
<div className='section-error-container'>
<h2>{title}</h2>
<p>{description}</p>
</div>
</section>
);
}
This is eventually propagated down to either node.before(section) or node.after(section).
I checked inside ReactDOM, ReactDOM/server and React with no luck. I saw I could create an HTML string, but I need an HTMLElement and would rather not do my own rendering if it can be avoided (I want to learn the React way, I already know the vanilla way).
My end goal is to learn how and when to use React properly. I'd love to know the proper way, but insight, advice and workarounds are also greatly appreciated!
In React you rather want to create a custom component with a single argument which contains the corresponding properties:
// single argument contains all props
function ErrorSection({name, title, description}) {
return (
<section id={name} className='section-error'>
<div className='section-error-container'>
<h2>{title}</h2>
<p>{description}</p>
</div>
</section>
);
}
now you need to import ReactDOM and call render in order to show the component ErrorSecion with some specific property values inside a HTML node with the id #app. Make sure that your HTML document contains such a node.
import ReactDOM from "react-dom";
ReactDOM.render(
<ErrorSection name="..." title="..." description="..." />,
document.querySelector("#app")
);
Most of the react apps render some dynamically generated nested components into the DOM using a single empty HTML node inside the document body (e.g. div#app or div#root). So you most likely will only need to have a single ReactDOM.render call in your entire project.
First of all, component's name should be written in PascalCase.
In React, you should rethink the way you render elements.
There are different approaches for different purposes:
Pass components to the children prop
const Wrapper = ({ children }) => (
<div className="wrapper">
<h1>Wrapper heading</h1>
{children}
</div>
);
Now you can pass children to the wrapper this way:
const AnotherComponent = () => (
<Wrapper>
<div>Element that will be rendered where the children prop is placed</div>.
</Wrapper>
);
Pass components to custom props:
If you need to render many components in different spots, you can do this:
const MultiSpotComponent = ({ HeaderComponent, FooterComponent }) => (
<div>
{HeaderComponent}
<div>Some content</div>
{FooterComponent}
</div>
);
And then pass your components to the props the same way you do with attributes in HTML:
<MultiSpotComponent HeaderComponent={CustomHeader} FooterComponent={CustomFooter} />
Notice that I used self-closing tag for the component, because I don't render children inside it.
Render list
const AnotherComponent = () => {
const dynamicArray = ['some', 'dynamic', 'values'];
return (
<div>
{dynamicArray.map(value => <div key={value}>{value}</div>)}
</div>
);
};
I have described only 3 most-used approaches, but there are more ways to render elements. You can learn more at Official React Documentation

How to select the clickable parent in my React component even if I click on its children or anywhere else inside the parent

I have started an application which I want to work same as weather.com next 36 hours section. The idea is when you click on each weatherCard which has a seperate component in my app you will update the below section which is my weatherDetails component based on the selected weatherCard /weather box. So I made the entire component clickable by giving it the click event via props from my stateful component which is my weatherLocation component. This is my WeatherCard component:
const WeatherCard = (props) => (
<div id={props.date} className="weatherCard" onClick={props.clicked}>
<h2 className="cardDate">{props.date}</h2>
<h4>{props.forcast}</h4>
<div className="minmaxDeg">
<data>{props.min}</data>
<data>{props.max}</data>
</div>
<data>{props.rain}</data>
</div>
);
And here in render method in WeatherLocation component I loop through data coming from state and give props the WeatherCard component:
const WeatherCards = this.state.reports.map( report => {
return(
<WeatherCard
key={report.id}
{...report}
clicked={() => this.handleCardClick(event)}
/>
);
});
And this is the handleCardClick that I added for it just for testing:
handleCardClick = event => {
// const { reports , selectedCardInfo , activeCard } = this.state;
const selectedDate = document.getElementById(event.target.id);
console.log(event.target.id);
}
I don't want to use anchor tag as I don't need href. The click works fine by itself. But because I need to get the id of the parent which is the div with the class of weatherCard. At the moment when I click on other elements inside the card I cannot get the id because they are not the parent. The reason I need its id is when I get data with from the API I need a unique value for each card so that when you click on the card the data for that card will be shown in the other component which is the WeatherDetails component. But for now I need to be able to somehow choose that selected card and pull out the state for that unique card. Could someone help me out? Thanks.
You just need to pass the Parent component ID to your onClick function in Weather Card.
Here is your WeatherCard - Component
const WeatherCard = (props) => (
<div id={props.date} className="weatherCard" onClick={event => props.clicked(event, props.id)}>
<h2 className="cardDate">{props.date}</h2>
<h4>{props.forcast}</h4>
<div className="minmaxDeg">
<data>{props.min}</data>
<data>{props.max}</data>
</div>
<data>{props.rain}</data>
</div>
);
You can see that I have added props.id to your onClick function and with help of event now you can access that id from the parent component.
Now here is your Parent Component- WeatherCards
const WeatherCards = this.state.reports.map( (report, i) => {
return(
<WeatherCard
key={report.id}
id={i}
{...report}
clicked={this.handleCardClick}
/>
);
});
You can see in the code I am passing index number as id to your child component.
So this will give you an id (for now it's an index number) of the card in your onClick handler.
and Finally, here is your on click handler.
handleCardClick = (event, weatherCardID) => {
console.log(weatherCardID)
}
As of now, I am using the index as id if you want to use a unique identifier, you can change that easily.
General JavaScript solution is to differentiate the elements and .stopPropogation after you've captured the event you are targeting. A nested unordered list, <ul>would be an example. Tag the containing <li> with an .opened class upon rendering/displaying each level of nesting, tag those <li> elements accordingly, e.g. a dataset attribute such as data-make, then data-model, then data-option. You then attach and fire event listeners on the different level <li>'s.
Thank you #RutulPatel. I made your answer as the answer. But I changed your code a bit as I got your point so I wrote an answer as it is long. I think we might not need to change the WeatherCard at all and I don't pass event or any logic there. so it will be intact:
const WeatherCard = (props) => (
<div id={props.date} className="weatherCard" onClick={event => props.clicked(event, props.id)}>
<h2 className="cardDate">{props.date}</h2>
<h4>{props.forcast}</h4>
<div className="minmaxDeg">
<data>{props.min}</data>
<data>{props.max}</data>
</div>
<data>{props.rain}</data>
</div>
);
But I use your tip changing my weatherCards array to look like this:
const weatherCards = this.state.reports.map( report => {
return(
<WeatherCard
key={report.id}
id={report.date}
{...report}
clicked={() => this.handleCardClick(event, report.date)}
/>
);
});
So I use the report.date which is a unique value as my id. Also I don't pass event as a parameter to the arrow function I just pass it with the report.date to the handler:
clicked={() => this.handleCardClick(event, report.date)}
And the handler will be the same as you did:
handleCardClick = (event, weatherCardID) => {
console.log(weatherCardID)
}
I might even remove event later on from both if there was no need fo that.
Thank you again.

Why does my React grandchild component not update after receiving a change in props from its parent? Or: do I need to use state?

I have tried finding the answer to this on StackOverflow and there are some related posts (e.g. React Child Component Not Updating After Parent State Change) but I want to understand why this is not working...
I have a React application that will display a layout of character cards (that is, each card displays a different character). It uses a child component, CharacterBoard, that lays out the CharacterCards, which would be a grandchild component. I pass the characters down from the App to the CharacterBoard as props, and CharacterBoard in turn maps these out the CharacterCards.
The problem is that I want the state of the character to change when I click on one of them. Specifically, I want the revealed field to change. However, even though the state change is reflected in the array of characters in the App (that is, the revealed field changes correctly), and the change is reflected in the array of characters in CharacterBoard, but not in CharacterCard. In fact, my mapping does not seem to be called at all in CharacterBoard when the props change.
Do I need to use something like getDerivedStateFromProps in CharacterBoard and set the state of that component and then use the state to map the values down to CharacterCard? If so, why?
In short (tl;dr), can you pass props on down through the component chain and map them out along the way and still have all changes reflected automatically?
Thanks for any guidance.
If it helps, the render method of my App is
render() {
const {state: {characters}} = this
return (
<div>
<header>
</header>
<main>
<CharacterBoard
onCardSelected={this.onCardSelected}
rowSize={logic.ROW_SIZE}
characters={characters}
cardSize={this.CARD_SIZE}/>
</main>
</div>
);
}
that of CharacterBoard is
render() {
const {props: {characters, rowSize, cardSize,onCardSelected}} = this
const rowUnit = 12 / rowSize
const cardLayout = characters
.map((character, i) => (
<Col xs={6} sm={rowUnit} key={character.name}>
<CharacterCard
onCardSelected = {onCardSelected}
key={i + Math.random()}
character={character}
cardSize={cardSize}
/>
</Col>
)
)
return (
<div>
<Container>
<Row>
{cardLayout}
</Row>
</Container>
</div>
)
}
and finally CharacterCard has this render method
render() {
const {props: {character, cardSize}} = this
const {thumbnail, revealed} = character
const imgURL = `${thumbnail.path}/${cardSize}.${thumbnail.extension}`
const topCardClass = classNames('characterCard__card-back', {'characterCard__card-back--hidden': revealed})
console.log(revealed)
return < a href="/#" onClick={this.onCardSelected}>
<div className='characterCard__card'>
<div className={topCardClass}>
<img src="/images/card_back.png" alt=""/>
</div>
< div className='characterCard__card-front'>< img alt=''
src={imgURL}/>
</div>
</div>
</a>
}
Doh! A simple forgetting to setState in App. Knowing that it should work made me go back through the code one more time and see that, indeed, it was a stupid error on my part.

Concatenate local path + dynamic images from Map - React.Js

i'm trying to do a CardList items from a local data.json file using React.Js.
To load the data i'm using a map function:
const ItemsList = data.map(item=>{
return(
<li><img key={item.id} src={"assets/images/sampleImages/" + item.image}/></li>
)
})
Code - PasteBin
but i cannot get the image. No error, just a broken image icon appear.
I've try:
This solution And with URL works, but don't work with path.
Also this thread. And nothing works.
Firstly import the image like this (You can amend it accordingly)
import imageBaseURL = "./assets/images/sampleImages/";
Then inside your ItemList make use of Template Literals like this :
const ItemsList = data.map( item => {
return(
<li><img key={item.id} alt="TitleOfImage" src={`${imageBaseURL}${item.image}`}/></li>
)
})
First step:
Importing image and store it in one variable ( ExampleImg )
import ExampleImg from '../example.png';
Second step:
Inject the image as a src
<img src = { ExampleImg } />

Categories