Passing Data to Another Component OnClick - javascript

So, I'm having a bit of an issue when it comes to routing and managing my data.
The Listing component is essentially a ton of cards with movie data that I am fetching from a gist of mine. I created a component for each card using .map and adding the value to the prop.
My goal: When I click a button for any of the cards, I would like to go to a page and access that movie card's name dynamically.
For Example::
Suppose I click on a button that says "MoreInformation" for one particular movie card. I get routed to the MoreInformation page because the button will be contained in a Link.
Inside the MoreInformation Page/Component I would like to dynamically add:
<h1>{name}</h1>
This is just a basic example of what I am trying to accomplish, but in other words how do I transfer the data of that particular movie card to the MoreInformation page so that I can access it to add dynamic data.
I will be extremely grateful if I can get feedback as this project is time sensitive... Thanks guys.
const MoviesPage = () => {
const [movies, setMovies] = useState([])
useEffect(() => {
axios
.get(
`https://gist.githubusercontent.com/ernestosotelo/9932c659b460e5ddeec8a8ae76164a0d/raw/ce8d7b248f61e73bf3c767538728505d6bac9835/json`
)
.then(res => {
const data = res.data
setMovies(data)
})
}, [])
return (
<Layout>
<div style={{ background: "hsl(215, 100%, 3%" }}>
<TopThree />
<p style={{ color: "#e0dfe2", fontSize: "1.7rem", marginTop: "3rem" }}>
<b
style={{
padding: ".5rem 1.5rem .5rem 1.5rem",
background: "#f9ba00",
fontSize: "2rem",
marginLeft: "4rem",
color: "black"
}}
>
!
</b>{" "}
Click 'Remove' to remove movies you definitely will not watch! There
is an undo option.
</p>
<div className={listingCSS.block}>
{movies.map(movie => {
return (
<Listing
key={movie.name}
name={movie.name}
plot={movie.plot}
date={movie.releaseDate}
genre={movie.genre}
imdbRating={movie.imdbRating ? movie.imdbRating : "N/A"}
tomatoRating={movie.tomatoRating ? movie.tomatoRating : "N/A"}
metaRating={movie.metaRating ? movie.metaRating : "N/A"}
erbertRating={movie.erbertRating ? movie.erbertRating : "N/A"}
tmdbRating={movie.tmdbRating ? movie.tmdbRating : "N/A"}
movshoRating={movie.movshoRating}
streamOn={movie.streamOn}
poster={movie.poster}
alt={movie.name}
/>
)
})}
</div>
</div>
</Layout>
)
}

You need to create a route for displaying the data passed. For example
<Route path="/movieInfo/:name" exact component={MoreInformation} />
In your listing create a link to more information component
<Link to={`/movieInfo/${this.props.name}`}>More info</Link>
In your MoreInformation component access the prop like
const { name } = this.props;
Simple Demo here
If you are using functional components, you can retrieve the value, like below. Inside match params.
function MoreInformation(props) {
const name = props.match.params.name;
return <h1>{name}</h1>;
}

You could use JS with HTML 5 elements to store the data on session (sessionStorage.getItem() and sessionStorage.setItem()) or locally (localStorage.getItem() and localStorage.setItem()).
If you prefer storing the data on setItem by variables :
var session_data = sessionStorage.myValue //For session only
var local_data = localStorage.myValue //For local
sessionStorage.myValue = 'value' //For session only
localStorage.myValue = 'value' //For local
Remember also : To store JavaScript objects you should set and change them to a string value :
sessionStorage.myObject = JSON.stringify(myObject); // Setting the object to a string
And parsing it back to object to retrieve the data when using getItem :
var myObject = JSON.parse(sessionStorage.myObject); // Parsing JSON back from string to object
ALSO : You could use window.name to store information, but note that it works only when the same window/tab is used, so make sure your button or link has the target value set to _self.
You can read about storing data without cookies with JavaScript HERE
For info about the Web Storage API : https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API
For info about PersistJS (might interest you) : https://github.com/jeremydurham/persist-js

Related

React.js - Done a API fetch that renders all items, now I want to be able to route on each item to their "own" page with JSON info

I have created an React site that renders all items inside an API Fetch on a page. What I want now is to be able to press each item that renders and be able to get routed to a new component that shows "more" info about that item.
Below I have the following code that takes in the "input" that you can use to search for all items or for a specific item.
const AgentSearch = (e) => {
e.preventDefault();
function capitalizeName(input) {
return input.replace(/\b(\w)/g, (s) => s.toUpperCase());
}
console.log('you hit search', input);
dispatch({
type: actionTypes.SET_SEARCH_TERM,
term: capitalizeName(input),
});
//do something with input
history.push('/findagent');
};
return (
<form className='search'>
<div class='search__input'>
<SearchIcon className='search__inputIcon' />
<input value={input} onChange={(e) => setInput(e.target.value)} />
</div>
Here is the code that renders all items:
eachName = data.map((item) => {
return (
<tr>
<td class='text-left'>{item.name}</td>
<td class='text-left'>{item.agency}</td>
</tr>
);
});
Basically what I would like to do is to "catch" the {item.name} and put that into a new query into the fetch once you press that item on the list that got created.
I tried to create a button that covers the whole class and then put {item.name} as input, but that does not work, also tried to make a "fake" input window that has the {item-name} stored for each item on list, even though the {item-name} gets onto the input window, once i push the button that its connected to, it says it doesn't have any value.
Does anyone know of any clean idea for this? I'm new to React so this might be really easy, haha.
Thanks!
The simplest way is to do onCLick on the td. See the code below.
const eachName = data.map((item) => {
return (
<tr>
<td class='text-left' onClick={() => handleNameClick(item.name)}>{item.name}</td>
<td class='text-left'>{item.agency}</td>
</tr>
);
});
You can define the handleNameClick function. You'll get the item.name as the parameter. See below.
const handleNameClick = itemName => {
//your code to send request for the clicked name
}

React router dom, component doesn't get unmounted when changing with id

I have a route like this: <Route exact path="/product/:id" component={Product} /> inside a Switch. Inside the component Product I have a custom hook that makes a request to my api. I also have another component inside Product called: Configurator.
Inside the Configurator component I make another api request to get a specific choice. Then inside the render of the Configurator component I check which component I need to render.
{loading ? <Loading /> :
choices.map((choice, index) => {
return (
<div
key={choice.id}
id={choice?.id}
ref={choice?.id == activeChoice ? myRef : empty}
className={`${choice?.id == activeChoice ? "active" : "inactive"} configurator__choice`}
onClick={() => enableChoice(choice?.id)}
>
{choice?.type === "parent_size" ? <ChoiceSize key={choice.id} number={index + 1} choiceData={choice} availableChoices={availableChoices} activeChoice={activeChoice} onChange={onChange} choices={choices} fetchNewChoiceData={fetchNewChoiceData} onSubmit={onSubmit} /> : <div></div>}
{choice?.type === "parent" ? <ChoiceOption key={choice.id} number={index + 1} choiceData={choice} availableChoices={availableChoices} activeChoice={activeChoice} choiceNameId={choice?.id} choiceId={choice?.choices[0]?.next.id} onClick={onClick} /> : <div></div>}
</div>
)
})
}
So when choice.type is equal to parent_size it loads the ChoiceSize component and adds data as props.
The problem I have is that whenever I am for example on /product/1 and I want to switch to /product/2 my old components (ChoiceSize and/or ChoiceOption) won't go away. It will add new choices with the data from product 2 but the old choices from product 1 are still there.
Does anyone know how I can fix this? Maybe that the whole component gets unmounted and the old choices will get removed and the new choices will get added

How to inject a dinamically created element into an existing div in React JSX?

I have a list of objects photos, from a json data file, that I would like to organize into 3 different <div> columns, but I dont know how to achieve that, here is my broken non-optimized code:
<div className="container">
<div ref={leftColRef} className="left-col" />
<div ref={centreColRef} className="centre-col" />
<div ref={rightColRef} className="right-col" />
{Object.keys(photos).forEach((n, i) => {
const id = photos[n].id;
const thumb = photos[n].thumbnailUrl;
const title = photos[n].title;
const element = (
<Thumbnail id={id} title={title} thumb={thumb} />
);
if (i % 3 === 0) {
leftColRef.current.append(element);
} else if (i % 3 === 1) {
centreColRef.current.append(element);
} else {
rightColRef.current.append(element);
}
// this line works, it idsplays the data but is commented as the data needs to go inside its respective columns
// return <Thumbnail key={id} title={title} thumb={thumb} />;
})}
</div>
The idea is to insert some elements into the left-column when i%3 = 0 and others in the centre-column when i%3 = 1 and so on ...
And a link to my codesandbox
Any help/advise will be much appreciated.
Easiest is probably to prepare the data outside the render function and to render the column one by one.
You should not manipulate the DOM like it's done in jQuery using JSX
Example:
const Component = (props) => {
const filterPhotos = (column) => {
return props.photos.filter((photo,index)=> index%3==column);
}
return <>
<MyColumn photos={filterPhotos(0)}/>
<MyColumn photos={filterPhotos(1)}/>
<MyColumn photos={filterPhotos(2)}/>
</>;
}
First, using ref on div to inject stuff on it is wrong. It's the opposite of how react works.
Like charlies said, I would split the photos in 3 different arrays before the render. Then, you'll be able to do something like this :
<div ref={leftColRef} className="left-col" />
{ photosLeft.map(photo => <Thumbnail key={photo.id} {...photo} />)
</div>
when preparing your data, try to use the same object properties and component props name so you can spread it easily ( {...photo} ).
Note: Also, when rendering an array in react, each child must have a unique key props. It will help react to render on that part of dom if your data change.

Error accessing filtered data in React Table

I am trying to use the above code, but I am getting an error during the build process saying this.selectTable is null.
Can anybody help me out with this?
I have followed this logic - Access filtered data in ReactTable
<ReactTableComponent
ref={this.reactTable}
className="polls-table"
defaultPageSize={10}
getTrProps={(_, rowInfo = {}) => {
let { index = 0 } = rowInfo;
return {
style: {
background: index % 2 ? "#fafafa" : "#FFFFFF"
}
};
}}
columns={columns}
data={polls}
/>
<Button
color="primary"
type="button"
onClick={download(this.reactTable.current.getResolvedState())}
>
Download
{/* <CSVLink data={myFunction(this.selectTable.getResolvedState())} filename="polls.csv">Export to CSV</CSVLink> */}
</Button>
</Paper>;
A problem is within this line:
onClick={download(this.reactTable.current.getResolvedState())}
What happens now is the JS running your code right away to calculate the onClick handler. And just because the ref on the first run is null, the error occurs.
What you should have instead is this:
onClick={() => {download(this.reactTable.current.getResolvedState())}}
You can also add condition to check wheter this.reactTable.current is not null

Passing back the result of a this.props.functionName() to parent

In React JS I have 2 files:
File 1: Chat.js
This file calls numerous custom components and provides them with custom functions as props.
File 2: PreviewChatBox.js
This file shows a preview of multiple chats. When I call the function that has been sent from chat.js (load Chat) it changes a state in Chat.js to an integer from one of the previewchat components.
I need to be able to access that component in Chat.js but I can't seem to be able to pass the variable back.
CODE:
File 1: Chat.js
render(){
return(
<PreviewChatBox onClicked={() => this.previewClicked()}/>
)}
File 2: PreviewChatBox.js
<div onClick={() => this.props.onClicked()} key={value}>
<p> {element[0]}{element[1]}{element[2]} </p>
</div>
So I can call the method without any problem but I cannot pass the key back to chat.js
Have you looked into using a callback function?
Chat.js
<PreviewChatBox
onClicked={() => this.previewClicked()}
updateKey((newKey) => this.setState({newKey}) />
Now your child component should be able to update the state of Chat.js by accessing the updateKey function from props.
Hope this helps match your use case, but I'm able to get this working by referencing the previewClicked method directly in Chat.js without using an arrow function and passing the key directly to the onClick behavior in PreviewChatBox.js
File 1 : Chat.js
previewClicked = (val) => {
console.log(val);
};
render() {
return (
<PreviewChatBox onClicked={ this.previewClicked }/>
)
};
File 2 : PreviewChatBox.js
const element = ['abc','def','ghi'];
const value = 2;
return (
<div onClick={ () => props.onClicked(value) } key={value}>
<p> {element[0]}{element[1]}{element[2]} </p>
</div>
);

Categories