React: Add active class to selected Nav link on click - reactjs

I'm trying to add an 'active' class to a nav link when it is clicked.
in JQuery I could do something like:
$('.navlink').on('click', function() {
$("a").removeClass('active');
$(this).toggleClass('active');
});
This is the closet I've managed to get in React:
export class NavLink extends Component {
constructor(props) {
super(props);
this.toggleClass=this.toggleClass.bind(this);
this.state = {
isActive: 1,
}
}
toggleClass = () => {
this.setState({ isActive: this.props.id });
}
render() {
const { href, name, id } = this.props;
const classNames = this.state.isActive === id ? "nav-link active" : "nav-link";
return (
<a
className={classNames}
onClick={this.toggleClass.bind(this)} href={href}>{name}</a>
)
}
}
The problem is React only re-renders the clicked on link. So once a link has been given a class of active it won't be removed if a different link is clicked.

You would need to do something like this in terms of components:
NavLinkList
|--NavLink
|--NavLink
NavLinkList would be the one holding the activeLinkId in its state.
That activeLinkId would then be passed to each Link as prop so when it is changed the Links would re-render.
Also in that NavLinkList you would have the function which would change the activeLinkId on the Links onClick handler.

I've done something like this before. It is easier to do this through the parent component. I didn't have components for each of my links. I just set their classes before the return in the render. Here is a sample of the code I did that I hope can be helpful to you.
I did the same thing with the onClick and the state just held a selected which held a String representing the name of the nav item. I had a Stories, Comments, and Likes nav links. Their original class was just 'tab'.
render () {
let storiesClass = 'tab';
let commentsClass = 'tab';
let likesClass = 'tab';
if (this.state.selected === "Stories") {
storiesClass += 'Selected';
commentsClass = 'tab';
likesClass = 'tab';
} else if (this.state.selected === "Comments") {
storiesClass = 'tab';
commentsClass += 'Selected';
likesClass = 'tab';
} else {
storiesClass = 'tab';
commentsClass = 'tab';
likesClass += 'Selected';
}
return (
<button className={storiesClass} onClick={this.selectTab("Stories")}>...

Related

React - Class component doesn't receive any params

I have a simple class Component:
class SearchedUserWrapper extends Component {
constructor(props) {
super(props);
this.state = {
searchedPhrase: "",
pageNumber: 1
};
this.increment = this.increment.bind(this);
}
GetUserForSearchResult = (postAmount, pageNumber) => {
const list = [];
for (let index = 0; index < postAmount; index++) {
list.push(<SearchResultUser CurrentPage={pageNumber}></SearchResultUser>);
}
return list;
}
increment = () => {
this.setState({ pageNumber: this.state.pageNumber + 1 })
console.log(this.state.pageNumber + 0);
}
render() {
return (<div>
{this.GetUserForSearchResult(5, this.props.pageNumber)}
<Button onClick={this.increment}> Current page {this.state.pageNumber}</Button>
</div>);
}
}
and function GetUserForSearchResult receives a state from SearchUserWrapper class. My SearchResultUser looks like this:
class SearchResultUser extends Component {
render() {
{console.log(this.props.CurrentPage)}
return (
<div className="user-searchresult">
{this.props.CurrentPage}
</div>);
}
}
export default SearchResultUser;
And console log says that this props are undefined, and the div is empty.
My goal is to have the effect that everytime I click "Current page" button, to refresh all the SearchResultUser component so that it displays a state passed as parameter. What am I doing here wrong? Why does it says to be undefined?
EDIT:
I tried couple of things and discovered something.
If I send the state in the params directly, for example:
render() {
return (<div>
<SearchResultUser CurrentPage={this.state.pageNumber}></SearchResultUser>
</div>
It seems to work, but the order of sending the state to the function, which passes it to params of component doesn't work.
GetUserForSearchResult = (postAmount, pageNumber) => {
const list = [];
for (let index = 0; index < postAmount; index++) {
list.push(<SearchResultUser CurrentPage={pageNumber}></SearchResultUser>);
}
return list;
}
render() {
return (<div>
<SearchResultUser CurrentPage={this.state.pageNumber}></SearchResultUser>
</div>);
Can somebody explain why is it happening like this?
Edit:
In this place(below) I think you have to pass state instead of props, because the main component(SearchedUserWrapper) doesn't receive any props, so this is undefined.
{this.GetUserForSearchResult(5, this.props.pageNumber)}
https://codesandbox.io/s/stackoverflow-71594634-omvqpw
First message:
Did you check if the page number was updated?
If the next state depends on the current state, the doc of react recommend using the updater function form, instead:
this.setState((state) => {
return {quantity: state.quantity + 1};
});
You should call super(props) before any other statement. Otherwise, this.props will be undefined in the constructor at SearchResultUser

How do I use a react.js button to modify a specific Firestore document?

I have written a react.js chat room that creates a new firestore document for every message. Each message has three fields: UID (from google), text, and createdAt. Text and UID are then displayed for every document, sorted by createdAt. I am now attempting to create a like button next to each message and a counter.
I added a likeCount field to each message. It is initially 0 and displays next to the text.
This looks like this:
function ChatMessage(props) {
const { text, uid, likeCount } = props.message;
const messageClass = uid === auth.currentUser.uid ? 'sent' : 'received';
return (<>
<div className={`message ${messageClass}`}>
<img src={photoURL || 'https://api.adorable.io/avatars/23/abott#adorable.png'} />
<div className = "likeCount">
<button onClick={something?}className="likeCount">
💡
</button>
</div>
<p>{likeCount}</p>
<p>{text}</p>
</div>
</>)
}
so... how can I make each button add one 'like' to the corresponding document?
Thank you for any help and insight. I am new to react.js!
Probably something like this:
class LikeButton extends React.Component {
state = {
likes: 0
};
render() {
return '<button>Likes: {this.state.likes} </button>'
}
}
addLike = () => {
let newCount = this.state.likes + 1;
this.setState({
likes: newCount
});
};
However, as you are new to React.js I would have a look at this helpful documentation to guide you with interacting with Firestore.
You can load your like count from firestore in a lifecycle method, most likely in componentDidMount and in componentDidUpdate. You can pass down a method for updating likes to the ChatMessage functional component from the parent component. Your button in the chat message component would look like this:
<button onClick={props.addLike} className="likeCount">
and an example of what your parent component could look like:
constructor(props) {
this.state = {
chatMessages: []
}
}
async componentDidMount() {
const chatMessages = (await chatMessages.get()).data();
this.setState({ chatMessages });
}
addLike(doc) {
doc.update({likeCount: doc.likeCount + 1});
}
render() {
return (
this.state.chatMessages.map((chatMessage) => {
return (
<ChatMessage
chatMessage={chatMessage}
addLike={this.addLike}
/>
)
}
)
}
Keep in mind this is similar to what you would do, not exactly. Consider this pseudocode.

How to store jsx into a variable using react?

I am new to programming. I want to implement the below,
when I click a list item I want to navigate to another page.
What I am trying to do? I have list items in a side panel. when I click on the list item it should navigate to another page.
So I have a listpanel component which renders the listitem component. On click list item, based on item_data_type it should take to the link got from get_type1_link method. however, it returns an object. I am not sure where I am making mistake.
class ListPanel extends react.purecomponent {
get_type1_link = () => {
const item_data = this.props.item_data;
const itemss = this.props.items;
const {itemname, item_id} = item_data.properties;
const filtered_item = items && items.find(item => item.id ===
item_id);
const item_name = (filtered_item) ? filtered_item.itemname :
(itemname ? itemname : item_id);
if (filtered_item) {
return (<Link to={`/someurl/${item_data.properties.item_id}`}>
{item_name}</Link>);
} else {
return <span>{item_name}</span>;
}
};
get_link = () => {
const item_data = this.props.item_data;
let link;
switch (item_data.type) {
case 'type1':
link = this.get_type1_link();
break;
case 'type2':
link = this.get_type2_link(); //some method similar to
//get_type1_link method
break;
default:
return link=window.location.href;
}
return link;
};
render = () => {
const list_item = this.props.;
return (
<ListItem
key={list_item.id}
text={this.get_text}
link={this.get_link}/>
);
}
class ListItem extends react.purecomponent {
render = () => {
<li onClick={props.link}>
<div className="text">
{this.props.text}
</div>
</li>
}
}
I think there is a problem in the way I am storing the value returned from get_type1_link method into variable link. since get_type1_link returns a jsx (Link). Could someone help me with this thanks.
I think issue is with your extends,
class ListPanel extends react.purecomponent {
It should be
class ListPanel extends React.PureComponent {
And also another issue is your render function, you have not return anything. Your render function should return like,
class ListItem extends React.PureComponent {
render = () => {
return(
<li onClick={this.props.link}> //props.link should be this.props.link
<div className="text">
{this.props.text}
</div>
</li>
)
}
}

Passing a value from a child component to another one (same level) without waiting for parent status update

I do not have a lot of experience with React so here it goes.
I am trying to build a simple website, with multi language support.
For this, I have the parent component (App), with 2 child components (LanguageSelector and Menu).
What I want to achieve: when I select the language from the LanguageSelector component, the text existing in the Menu component is updated depending on the language selected.
What is happening?
I open the web site, the Menu component is filled with the text defined in the default language
When I change the language, the parent component (App) receives the new language from the child
The Menu child is updated with the previous language instead of the selectedLanguage because the parent component is only updated when all the child are updated.
What I would like to know:
What am I doing wrong?
Is this the global right way to do this?
How can I pass the selected language from the LanguageSelector child component to the Menu simbling child component?
App.js
class App extends Component {
constructor(props) {
super(props);
this.state = {
languages: ["FR", "DE", "GB"],
currentLanguage: 'FR'
};
this.onLanguageChange = this.onLanguageChange.bind(this);
}
onLanguageChange = (currentLanguage) => {
this.setState({
currentLanguage: currentLanguage
});
}
render() {
return ( <
div className = "App" >
<
LanguageSelector languages = {
this.state.languages
}
currentLanguage = {
this.state.currentLanguage
}
onLanguageChange = {
this.onLanguageChange
}
/> <
Menu size = {
[1000, 500]
}
onLanguageChange = {
this.onLanguageChange
}
currentLanguage = {
this.state.currentLanguage
}
/> <
/div>
);
}
}
export default App;
LanguageSelector.js
LanguageSelector extends Component {
constructor(props) {
super(props);
this.state = {
languages: this.props.languages,
currentLanguage: this.props.currentLanguage
};
this.setCurrentLanguage = this.setCurrentLanguage.bind(this);
}
render() {
return ( <
div >
<
span className = "languageSelection" >
<
ReactFlagsSelect defaultCountry = "FR"
searchable = {
false
}
countries = {
this.state.languages
}
onSelect = {
this.setCurrentLanguage
}
ref = "countryCode" / >
<
/span> <
/div>
);
}
export default LanguageSelector;
I think you just need to pass the languageSelector change event up to the parent (App) and this should then rerender the menu - like so:
LanguageSelector:
<span className="languageSelection">
<ReactFlagsSelect defaultCountry="FR" searchable={false} countries={this.state.languages}
onSelect={val => this.props.onLanguageChange(val)} ref="countryCode"/>
</span>
Note: you might need to check what onSelect returns and adjust accordingly.
Have a look at react-redux. Otherwise, since you are controlling currentLanguage in the state of the parent component, passing the value as props will work perfectly fine. Don't reference the currentLanguage and languages props as a state in your LanguageSelector component, this can yield problems.
Try using the code below for your LanguageSelector component definition.
class LanguageSelector extends Component {
render() {
return (
<div>
<span className="languageSelection">
<ReactFlagsSelect defaultCountry="FR" searchable={false} countries={this.props.languages} onSelect={this.props.onLanguageChange} ref="countryCode"/>
</span>
</div>
);
}
And then make sure you pass the currentLanguage variable from App state to the Menu component.
<Menu onLanguageChange = {this.onLanguageChange}
currentLanguage={this.state.currentLanguage}/>
And your code for the Menu component should then call currentLanguage from props.
class Menu extends Component {
constructor(props) {
super(props);
}
onLanguageChange(language) {
this.props.onLanguageChange(language);
}
render() {
return <div className="menu">
<h1>{this.props.currentLanguage}</h1>
</div>
}
}
I've made the modification suggested:
<ReactFlagsSelect defaultCountry="FR" searchable={false} countries={this.props.languages} onSelect={this.props.onLanguageChange}/>
When I select a new language, the function onLanguageChange on the parent (App.js) is correctly called and the value correct (the new language selected).
onLanguageChange = (currentLanguage) => {
console.log("App onLanguageChange: " + currentLanguage);
this.setState({ currentLanguage: currentLanguage});
}
However, how can I say to the other child to use this received language?
<Menu ???/>

How to add multiple onClick function on a button in ReactJS

I have a tabs.js file that needs to be connected with some data. There are 4 tabs under tabs.js and on clicking each button some data should appear. I already have some onClick functionality on the 4 tabs. I cant get hold of how can i further add another functionality to each tab so that onclick of each tab, data should appear.
Please suggest a simple solution.
Here is my piece of code:
import React, { Component } from 'react'
class Tabs extends Component {
constructor(props) {
super(props);
this.state = { selectedTabIndex: -1};
this.handleChange = this.handleChange.bind(this);
}
handleChange(index) {
this.setState({ selectedTabIndex: index});
}
render() {
const { selectedTabIndex } = this.state;
const { tabs } = this.props;
return (
<ul className="tabs padding-left-large padding-top-small">
<li className={`tab-title ${selectedTabIndex==0?'active':''}`}><button onClick={()=>{this.handleChange(0)}}>{tabs[0]}</button></li>
<li className={`tab-title ${selectedTabIndex==1?'active':''}`}><button onClick={()=>{this.handleChange(1)}}>{tabs[1]}</button></li>
<li className={`tab-title ${selectedTabIndex==2?'active':''}`}><button onClick={()=>{this.handleChange(2)}}>{tabs[2]}</button></li>
<li className={`tab-title ${selectedTabIndex==3 || selectedTabIndex==-1?'active':''}`}><button onClick={()=>{this.handleChange(3)}}>{tabs[3]}</button></li>
</ul>
);
}
}
export default Tabs
As long as you're passing parameters to the function to me it looks like the easiest way to get each individual button to do something else would just be something like this:
handleChange(index) {
if(index === 1){
//do something for tab 1
}
if(index === 2){
//do something for tab 2
}
//etc.
this.setState({ selectedTabIndex: index});
}

Resources