Get element was clicked in react using ref - reactjs

I want to use react ref to get the element that was clicked, how i can do that and replace this javascript code in handleFavoritePairs function.
handleFavoritePairs = (e) => {
const pairs = e.target.closest('.coin-start').parentNode.getAttribute('symbol');
console.log(pairs);
}
render() {
coins[this.props.coin].map(coin => {
return (
<div className="landing-ticker-row" symbol={coin[0]} key={coin[0].toString()}>
<div className="coin-start" onClick={this.handleFavoritePairs}>
<i className="fas fa-star"></i>
</div>
<div className="coin-symbol">{coin[0]}</div>
</div>
);
})
}

There is no reason to use ref here and it gets tricky when mapping through arrays. Just make another component and pass the symbol down to that component. Then pass your onclick function down as well. Then you can pass the symbol back up to the parent when the user clicks on it like so:
handleFavoritePairs = symbol => {
console.log(symbol);
}
render() {
coins[this.props.coin].map(coin => {
return (
<NewComponent
key={coin[0].toString()}
symbol={coin[0]}
handleFavoritePairs={this.handleFavoritePairs}
/>
);
})
}
//new component
const NewComponent = props => (
<div className="landing-ticker-row">
<div className="coin-start" onClick={() => props.handleFavoritePairs(props.symbol)} >
<i className="fas fa-star"></i>
</div>
<div className="coin-symbol">{props.symbol}</div>
</div>
);
As you can see you are passing the symbol down to the new component along with your handleFavoritePairs function. Then you can pass the symbol into the onclick handler in your new component and pass it back up to your main component when the user clicks on it.

Related

How to change a style of an HTML element in React?

I have two React components
class App extends React.Component {
render() {
return (
<div id="appWrapper">
<ConfigureWindow />
<button id="configureClocksButton">Configure clocks</button>
<section id="clocksHere"></section>
</div>
);
}
}
const ConfigureWindow = () => (
<div id="configureWindowWrapper">
<div id="configureWindow">
<section id="addCitySection">TODO: adding a city</section>
<div id="verticalLine"></div>
<section id="listOfCities">
<header>
<h1>Available cities</h1>
<div id="closeConfigureWindowWrapper">
<img src="..\src\images\exit.png" id="closeConfigureWindow" alt="" />
</div>
</header>
<section id="availableCities"></section>
</section>
</div>
</div>
);
I want "ConfigureWindow" to be shown when "configureClocksButton". I tried to execute it with props, state and a function but got errors. It also would be nice if you explain me how to create new React components with React functions?
You probably want to use the React.JS event onClick (https://reactjs.org/docs/handling-events.html), and a state to store the action. To create a function component, you just have to return the JSX you want to render, and use hooks (https://reactjs.org/docs/hooks-intro.html) and then do a conditional rendering (https://reactjs.org/docs/conditional-rendering.html):
const App = () => {
const [toggleConfiguration, setToggleConfiguration] = useState(false)
return (
<div id="appWrapper">
{toggleConfiguration && <ConfigureWindow />}
<button onClick{() => setToggleConfiguration(true)} id="configureClocksButton">Configure clocks</button>
<section id="clocksHere"></section>
</div>
);
}
It's a bit difficult to understand your post, but I gather you want to click the button with id="configureClocksButton" and conditionally render the ConfigureWindow component.
You can accomplish this with some boolean state, a click handler to toggle the state, and some conditional rendering.
class App extends React.Component {
this.state = {
showConfigureWindow: false,
}
toggleShowConfigureWindow = () => this.setState(prevState => ({
showConfigureWindow: !prevState.showConfigureWindow,
}))
render() {
return (
<div id="appWrapper">
{showConfigureWindow && <ConfigureWindow />}
<button
id="configureClocksButton"
onClick={this.toggleShowConfigureWindow}
>
Configure clocks
</button>
<section id="clocksHere"></section>
</div>
);
}
}
A function component equivalent:
const App = () => {
const [showConfigureWindow, setShowConfigureWindow] = React.useState(false);
const toggleShowConfigureWindow = () => setShowConfigureWindow(show => !show);
return (
<div id="appWrapper">
{showConfigureWindow && <ConfigureWindow />}
<button
id="configureClocksButton"
onClick={toggleShowConfigureWindow}
>
Configure clocks
</button>
<section id="clocksHere"></section>
</div>
);
}

Reset component into modal

I have a component that is inserted in a modal and that includes a CheckListBox. When the modal starts each time, the component is not reset. How can I do? How Force reset? I use reactjs with hooks.
How can I trigger a reset event every time the modal opens?
Thanks a lot.
const CheckList = ({title, api, color, onChange }) => {
const [items, setItems] = useState([]);
let listCheck = [];
useEffect(() => {
axiosApi.get( `${api}`).then((res)=>{
setItems(res.data);
})
}, [])
function handleClick(ev, item) {
if (ev.target.checked) {
listCheck.push(item)
onChange(listCheck);
}
else
{
listCheck = listCheck.filter(riga => {
return (riga.id !== item.id)});
onChange(listCheck);
}
}
return (
<>
<div class="card rd-card-list">
<div class="card-header">
{title}
</div>
<div class="card-content rd-card-content">
<div class="content rd-scroll">
<ul class="rd-ul">
{ items.map( (item) =>
<li class="rd-li" key={item.id}>
<label class="checkbox">
{item.description}
</label>
<input type="checkbox" onClick={(ev) => handleClick(ev, item)}/>
</li>
)
}
</ul>
</div>
</div>
</div>
</>
);
}
export default CheckList;
in my modal.js
<CheckList title="mytititle" api="/api/users" onChange={(itx) => {
formik.setFieldValue('users', itx)
} }/>
The easiest way is to not render the modal until it's open:
<div>
{modalOpen &&
<Modal open={modalOpen}>
<CheckList title="mytititle" api="/api/users" onChange={(itx) => {
formik.setFieldValue('users', itx)
} }/>
</Modal>
}
</div>
So whenever you close the modal, it will be removed from DOM, along with any data that this component had.
React life cycle events can be used to perform operation before a component can be rendered. 'constructor()' or 'componentDidMount()' can be used in class components to reset the data or any other operation before rendering the component.
Since you are using function component, you can use React hooks to mimic the life cycle events using 'useEffect()'.

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.

Accessing props to mapped objects in render method [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 6 years ago.
I'm trying to create a dynamic list of views managed in a home component that can be saved and loaded. The save and load functions work fine but I get the following error when calling it in the span's returned from the map function in the Footer.js render method
Uncaught TypeError: Cannot read property 'props' of undefined
How can I access this function call in these elements?
Footer.js
export default class Footer extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="footer hbox">
<div className="views hbox">
<span onClick={ (e) => this.props.getViewNames() }>Get Views</span>
<span onClick={ (e) => this.props.saveView('test3')}>Save View</span>
<span onClick={(e) => this.props.loadView('test3')}>Load View</span>
{this.props.getViewNames().map(function(viewName){
return (
<span onClick={(e)=>this.props.loadView(viewName)}>
{viewName}
</span>
);
})}
</div>
);
}
}
You forgot to use an arrow function for getViewNames.map(), and thus the lexical scope of this is not kept in your code -- which is interesting, considering you did use the correct approach everywhere else.
So just do:
this.props.getViewNames().map(viewName => {
...
});
Also see:
How to access the correct `this` context inside a callback?
The reason you cannot access props inside the map function is because this inside your function refers to the context of the map function, and not React Component. You will need to bind your map function to the React Component context by using bind(this) on the map function or arrow functions
Using bind(this)
export default class Footer extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="footer hbox">
<div className="views hbox">
<span onClick={ (e) => this.props.getViewNames() }>Get Views</span>
<span onClick={ (e) => this.props.saveView('test3')}>Save View</span>
<span onClick={(e) => this.props.loadView('test3')}>Load View</span>
{this.props.getViewNames().map(function(viewName){
return (
<span onClick={(e)=>this.props.loadView(viewName)}>
{viewName}
</span>
);
}.bind(this))}
</div>
);
}
}
using arrow function
export default class Footer extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="footer hbox">
<div className="views hbox">
<span onClick={ (e) => this.props.getViewNames() }>Get Views</span>
<span onClick={ (e) => this.props.saveView('test3')}>Save View</span>
<span onClick={(e) => this.props.loadView('test3')}>Load View</span>
{this.props.getViewNames().map((viewName) => {
return (
<span onClick={(e)=>this.props.loadView(viewName)}>
{viewName}
</span>
);
})}
</div>
);
}
}

Resources