Conditionally rendering component on button click (React) - javascript

I have created a basic React project that is pulling data from a SQL server. I would like this to be able to be rendered conditionally depending on what button has been clicked.
This is my Display Users Component which is used within my AdminViewUsers component (What is actually displaying the users).
import React, { Component } from 'react';
import './customers.css';
class DisplayUsers extends React.Component{
constructor(){
super();
this.state= { users: [] }
}
componentDidMount(){
this.setState({
users: this.getItems()
})
}
getItems(){
fetch('/admin-view-users')
.then(recordset => recordset.json())
.then(results => { console.log(results.recordset); this.setState({'users': results.recordset}); });
}
render () {
console.log(this.state.users)
return (
<ul>
{this.state.users && this.state.users.map(function(user, index){
//if (user.severity === 1){
return(
<div className ="jumbotron">
<li>
Severity: {user.severity}
</li>
<li>
<p>User Name:{user.name} </p>
</li>
<li>
User Email: {user.email}
</li>
<li>
Description of Issue: {user.description}
</li>
<button>See Details</button>
</div>
)
})
}
</ul>
);
}
}
export default DisplayUsers;
This is my AdminViewUsers Component
import logo from '../codestone logo.png';
import {Link } from 'react-router-dom'
import '../bootstrap.min.css'
import '../bootstrap.min.css'
import '../App.css'
import Customers from "../Components/customers";
import DisplayUsers from "../Components/DisplayUsers";
import { ButtonDropdown, DropdownToggle, DropdownMenu, DropdownItem, DropDownButton } from 'reactstrap';
function Home() {
return (
<div>
<Header/>
<SeveritySelector/>
<DisplayUsers/>
</div>
);
}
function Header(){
return (
<div class="jumbotron">
<div className = "User-Menu">
<Link>User details </Link>
</div>
<img className='profile-image' alt='icon' src={logo} width="340" height="60"/>
<Navigation/>
</div>
)
}
function Navigation (){
return(
<div>
<br/>
<div class="btn-group">
<Link to= '/home'><button type="button" class="btn btn-light">Home</button></Link>
<Link to= '/admin-view-users'><button type="button" class="btn btn-light">View Users(Admin)</button></Link>
</div>
</div>
)
}
function SeveritySelector (){
return(
<div className = "Severity-Toolbar">
<div class="btn-toolbar" role="toolbar" aria-label="Toolbar with button groups">
<div class="btn-group mr-2" role="group" aria-label="First group">
<button type="button" class="btn btn-secondary">Severity High</button>
<button type="button" class="btn btn-secondary">Severity Medium</button>
<button type="button" class="btn btn-secondary">Completed</button>
<button type="button" class="btn btn-secondary">View All</button>
</div>
</div>
</div>
)
}
export default Home;
Essentially I would like to use the function Severity Selector to be the decider of how the statement is displayed.E.g If the high severity button is selected then it will display all with a high severity (1) if medium selected all with medium severity (2) and completed to have a severity of 3. Finally a button to display all.
What in your opinion is the best way to do this? I understand I could use multiple statements within my "server.js" and load different queries and have them connected to different pages.
But is there a way that I could just use a if statement or something similar to determine what button is selected to avoid multiple transactions with the server? You can see a brief attempt I had within the display users with an if statement which worked but just was not dependent on the buttons.

Conditional render can be achieved using various techniques, the most used is the bracket style variant. It can be used in the following way:
function Header(){
const showFirst = true;
return (
<div class="jumbotron">
{showFirst && <MyFirstComponent />}
{!showFirst && <MySecondComponent />}
</div>
)
}
This will render the <MyFirstComponent /> if showFirst is true and will show <MySecondComponent /> if it is false.

Related

how to re-use my modal component ( re-render issue) in vue js

I'm opening the same component content since i have a different ones, ( Rendering problem )
I have a modal component called modal.vue (Re-usable)
so each time I create a component I call the modal component and I inject new content,
The problem is : I have two-component ( country.vue & city.vue ) I import both of them in my index.vue
but each time I click on a city button to load city component, I load the country modal, just like there is a rendering problem ( i can't re-render )
can someone explain to me the solution please, this is my code
modal.vue ( re-usable )
<template>
<div class="right-bar" :class="(size == null) ? 'right-bar-35' : size">
<simplebar class="h-100">
<div class="rightbar-title px-3 py-4">
<div class="row">
<div class="col-lg-10">
<h5 class="m-0">{{title}}</h5>
</div>
<div class="col-lg-2 text-right">
<span class="clickable" #click="hideRightSidebar"><i class="mdi mdi-close-thick"></i></span>
</div>
</div>
</div>
<div class="px-3 py-4">
<slot/>
</div>
<footer class="modal-footer">
<button type="button" class="btn btn-secondary" style="float:left;">Cancel</button>
<slot name="footer"/>
</footer>
</simplebar>
</div>
</template>
as you can see i have a <slot/> in my component so each time in inject a new content.
This is my country.vue component ( i use here modal.vue component )
<template>
<div>
<button class="btn btn-sm btn-white" #click="init">Filter <i class="mdi mdi-filter"></i></button>
<modal title="countries" v-if="showModal">
i 'am the country modal
</modal>
</div>
</template>
<script>
import RightBar from "#/components/modal";
export default {
data() {
return {
showModal: false
}
},
components:{
modal,
},
methods: {
init: function(){
this.showModal= true;
}
}
}
</script>
This is my city.vue component ( i use here modal.vue component )
<template>
<div>
<button class="btn btn-sm btn-white" #click="init">Filter <i class="mdi mdi-filter"></i></button>
<modal title="cities" v-if="showModal">
i 'am the citymodal
</modal>
</div>
</template>
<script>
import RightBar from "#/components/modal";
export default {
data() {
return {
showModal: false
}
},
components:{
modal,
},
methods: {
init: function(){
this.showModal= true;
}
}
}
</script>
This is my index.vue ( where i load city.vue & country.vue ) as you can see both of my components have a button
<template>
<div>
<city></city>
<country></country>
</div>
</template>
<script>
import addContact from "./city.vue";
import filterContact from "./country.vue";
export default {
components:{city,country}
data(){
return {
}
}
}
</script>
So when i click on city i see the country modal ( re-rendering problem ) how can i solve that
Here's an example:
in city and country component add a function that tells the parent component that there will be a rerendering. you call this function once you click on the show modal button.
init(){
this.rerender()
this.showModal= true
},
rerender(){
this.$emit('forceRerender', true)
}
then in the parent component we'll listen to this function and rerender.
<city v-if="isCountry" v-on:forceRerender="hideCountry" />
<country v-if="isCity" v-on:forceRerender="hideCity" />
data(){
return{
isCountry: true,
isCity: true,
}
},
methods:{
hideCountry(){
this.isCountry= false
this.$nextTick(()=>{
this.isCountry= ture
})
},
hideCity(){
this.isCity= false
this.$nextTick(()=>{
this.isCity= ture
})
}
}
to break it down. when we want to load the modal from the city component we tell the country component to close the modal first. I hope this works for you.
another way with a switch statement:
city:
rerender(){
this.$emit('forceRerender', 'city')
}
country:
rerender(){
this.$emit('forceRerender', 'country')
}
parent component:
<city v-if="isCountry" v-on:forceRerender="setActiveElement" />
<country v-if="isCity" v-on:forceRerender="setActiveElement" />
setActiveElement(element){
switch(element){
case 'city':
this.isCity = true
this.isCountry = false
this.$nextTick().then(()=>{
this.isCountry = true
})
break
case 'country':
this.isCountry = true
this.isCity = false
this.$nextTick().then(()=>{
this.isCity= true
})
break
default:
this.isCountry = this.isCity = true
break
}
}

React map adding background color special elements

I use map but I have one problem I am trying to set the background of a certain element inside the map, in my case it is "item .title" I want my element to look like this https://ibb.co/ccJzD6g as you can see in the picture background color " 100% "which I specifically set in the console for but in fact if I set" background: orange "for the sidebar_list class, then the color will be applied to all elements including the rest , https://ibb.co/ZBTX3hd and if I set the background for the titleName class it looks like this https://ibb.co/84RKBBw but I want it to look like this https://ibb.co/ccJzD6g how do i solve this problem?
Lesson.jsx
import React from 'react';
import less from "./css/lesson.module.css";
import "./css/betaLesson.css";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { Navbar } from "../../../Navbar/Navbar";
export class Lessons extends React.Component {
constructor(props) {
super(props);
this.state = {
indexDescription: 0,
}
}
render() {
const listLessons = this.props.lesson.map((item, index) => {
return (
<li key={index} className={less.sidebar_list} onClick={() => {
this.setState({ indexDescription: index })
}}>
<div>
<FontAwesomeIcon className={less.item_icon} icon={item.iconName} />
</div>
<div style={{background: "orange"}} className={less.titleName}>
<p>{item.title}</p>
</div>
<div className={less.titleName}>
<p>{item.titleName}</p>
</div>
</li>
);
});
return (
<>
<div className="abc">
<Navbar color="blue" bg="tomato" centerFlexNavbarContainer="flex"
navbarSearchPage="Search" navbarHomePage="Home" centerHeadlineNavbarColumn="center" />
<div className={less.wrapper}>
<div>
<div className={less.sidebar}>
<div>
<ul>
{listLessons}
</ul>
</div>
</div>
</div>
<div className={less.main_content}>
<div className={less.main_inside_content}>
<div className={less.header}>
<div>
<h2>JavaScript JSON Reference</h2>
</div>
</div>
<div className={less.info}>
<div className={less.description}>
<p>
{
this.props.lesson[this.state.indexDescription]["description"]
}
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</>
);
}
}
First question answer: Create a new component with its own attributes separately and map/passdown the properties to the component.
Here's an example: https://codesandbox.io/s/objective-hopper-2st8g?file=/src/Lesson.js

<select> tag is not showing if I apply the select picker class, that I need

I need to retrieve the data from the second select , the one where I inserted the class select picker , but the data are not showing when I try to use this bootstrap-select component, the component is working properly I tried but I think that something is wrong with the insertion between the two select tags.
Or if anyone knows how can I create a multiselect for that skills will be great. Basically when a student selects a topic then a list of skills will appear and he can select multiple. Now I am trying with the react-bootstrap-multiselect that tadeo suggested but I have similar problem. Thanks in advance .(I have re-modified the code)
import UserContext from "./context/userContext";
import "../styles/slide_left.css";
import { trackPromise } from "react-promise-tracker";
import { getTopics} from "./../services/topicService";
import _ from "lodash"
import Multiselect from "react-bootstrap-multiselect";
class Topic extends Component {
state = {
topics: [],
selectedTopic: {}
}
async componentDidMount() {
const {data: topics} = await trackPromise(getTopics());
this.setState({topics});
}
selectTopicHandler = (e) => {
const selectedTopic = this.state.topics.filter(topic => topic.title === e.target.value)
this.setState({selectedTopic})
}
render() {
const {topics} = this.state
return (
<UserContext.Consumer>
{(user) => (
<main
role="main"
className="slide-left col-md-9 ml-sm-auto col-lg-10 px-md-4 "
>
<div className="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 className="h2">Topic and Skill selection</h1>
<div className="btn-toolbar mb-2 mb-md-0">
<div className="btn-group mr-2">
<button
type="button"
className="btn btn-sm btn-outline-secondary"
>
Edit
</button>
<button
type="button"
className="btn btn-sm btn-outline-success"
>
Save
</button>
</div>
</div>
</div>
<div class="form-preferences" > <div className="form-group">
ID: {user.name} <div>Email: {user.email}</div>
</div>
<form action="" class="form-preferences">
<div className="form-group">
<h5>Select your Topic</h5>
<select name="topic" id="topic" className="form-control" onChange={this.selectTopicHandler} >
{this.state.topics.map(topic=><option>{topic.title}</option>)}
</select>
</div>
{!_.isEmpty(this.state.selectedTopic) && <React.Fragment> <h5>Select your Skills</h5>
<Multiselect
options={this.state.selectedTopic[0].skills.map(skill=><option key={skill} >{skill}</option>)}
/>
</React.Fragment>}
</form>
</div>
</main>
)}
</UserContext.Consumer>
);
}
}
export default Topic;
there is an npm package https://www.npmjs.com/package/react-bootstrap-multiselect that might be helpful, if you want to create a multiselect. Install it like so:
npm install react-bootstrap-multiselect
It has a React Class called Multiselect.
import Multiselect from 'react-bootstrap-multiselect';
Then you can use the class to render the react component like so:
<Multiselect/>
It requires the bootstrap css to be active but that should already be the case in your project.

Using react how do i get the value of a select dropdown that is held in a const?

I am learning React. I have tried to keep components in separate files. So, I have:
SaveDocument (class)
PersonList (const)
Person (const)
PersonList represents a dropdown of persons. I am trying to figure out how to get the value of the select dropdown in the SaveDocument class (i.e. when they click 'Save Changes').
How can i get the value of the select dropdown when the user clicks click Save?
Code below:
PersonList.js
import React from "react";
import Person from "./../model/Person";
const PersonList = props => {
return (
<div key="PersonList">
<select className="col-6">
{props.persons.map(person => <Person key={person.id} {...person} />)}
</select>
</div>
);
};
export default PersonList;
Person.js
import React from "react";
import moment from "moment";
import "react-datepicker/dist/react-datepicker.css";
const Person = person => {
console.log(JSON.stringify(person));
return (
<option id="{person.id}">{person.firstName + " " + person.lastName}</option>
);
};
Document.defaultProps = {
firstName: "",
lastName: ""
};
export default Person;
SaveDocument.js
import React, { Component } from "react";
import postDocument from "./../rest/PostDocument";
import fetchPersons from "./../rest/FetchPersons";
import PersonList from "./../components/PersonList";
import ShowDatePicker from "./../components/ShowDatePicker";
class SaveDocument extends Component {
state = {
persons: [],
personFromSelect: ''
};
cachePersons = personInfo => {
console.log(">> persons" + personInfo);
this.setState(prevState => ({
persons: personInfo
}));
};
resetFields () {
console.log("reset");
console.log(this.keys.PersonList.value);
}
componentWillMount() {
console.log("mounted");
fetchPersons.callApi(this.cachePersons);
}
render() {
return (
<div
className="modal fade"
id="basicExampleModal"
tabIndex="-1"
role="dialog"
aria-labelledby="exampleModalLabel"
aria-hidden="true"
>
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title" id="exampleModalLabel">
Save document
</h5>
<button
type="button"
className="close"
data-dismiss="modal"
aria-label="Close"
>
<span aria-hidden="true">×</span>
</button>
</div>
<div className="modal-body">
<div className="row">
<div className="col-4 text-left">Document Date:</div>
<div className="col-6">
<ShowDatePicker />
</div>
</div>
<br />
<div className="row">
<div className="col-4 text-left">Person From:</div>
<PersonList persons={this.state.persons} />
</div>
<br />
<div className="row">
<div className="col-4 text-left">Comments:</div>
<div className="col-md-6">
<div className="form-group">
<input
type="text"
className="form-control"
id="commentsBox"
placeholder="Comments"
onKeyPress={event => {
if (event.key === "Enter") {
}
}}
/>
</div>
</div>
</div>
</div>
<div className="modal-footer">
<button
type="button"
className="btn btn-secondary"
data-dismiss="modal"
onClick={() => this.resetFields()}
>
Close
</button>
<button
type="button"
className="btn btn-primary"
onClick={() => postDocument.callApi(this.props)}
>
Save changes
</button>
</div>
</div>
</div>
</div>
);
}
}
export default SaveDocument;
In general uncontrolled components (where the input state is handled directly by the DOM element) are generally not advisable and make it harder to manage and reason about your app state. I'd recommend you change to a controlled component, where the state of your input is managed by React and the DOM simply renders that state.
PersonList.js:
Note that the <select> element receives its selected value from props, as well as a callback handler for when the user makes a change.
const PersonList = props => {
return (
<div key="PersonList">
<select className="col-6" value={this.props.value} onChange={this.props.onChangeCallback} >
{props.persons.map(person => <Person key={person.id} {...person} />)}
</select>
</div>
);
};
Person.js:
Note that it now has a value prop so that onchange events know what the new value will be, and <select> knows which option to display based on value.
const Person = person => {
console.log(JSON.stringify(person));
return (
<option value={person.id} id="{person.id}">{person.firstName + " " + person.lastName}</option>
);
};
SaveDocument.js:
Note that you're now keeping the dropdown select state in React state and passing it down to the child component PersonList, along with the callback handler for updating state.
...
onChangeCallback = (e) => {
this.setState({personValue: e.target.value});
}
cachePersons = personInfo => {
console.log(">> persons" + personInfo);
this.setState(prevState => ({
persons: personInfo,
personValue: personInfo[0].id
}));
};
render() {
...
<PersonList
persons={this.state.persons}
value={this.state.personValue}
onChangeCallback={this.onChangeCallback}
/>
...
}
Now you are actually keeping the select state of your dropdown menu in your parent component, SaveDocument, and passing it down into the list. The list simply renders the dropdown menu with the appropriately selected value (from state) and provides a callback for when it changes. Now the state of your dropdown lives inside React state and is easily accessible from inside SaveDocument when the user clicks the "save" button, instead of ambiguously living in the DOM element.
Add a prop to PersonList:
<PersonList onChangePerson={this.props.onChangePerson} />
Add an event handler for onChangePerson to SaveDocument.js and don’t forget to bind this in your constructor.
onChangePerson(event) {
var value = event.target.value
}
this.onChangePerson = this.onChangePerson.bind(this);
On the select add the onChange event
<select onChange={this.props.onChangePerson}></select>
You would then setState in the onChangePerson event to save your currently selected person and then when the user clicked Save, you would reference this.state.selectedPerson for example.

Switching between className 'active' in reactJS

I am getting the error "TypeError: Cannot read property 'isFollowing' of null" for the following code:
import React from 'react';
import styles from './Cover.css';
import withStyles from '../../../../decorators/withStyles';
import Link from '../../../../utils/Link';
import Avatar from './Avatar';
import classnames from 'classnames';
import { Button } from 'react-bootstrap';
#withStyles(styles)
class Cover extends React.Component {
toggleFollow () {
this.setState({isFollowing: !this.state.isFollowing});
}
render() {
var user = this.props.user;
var followClass = this.state.isFollowing? 'active': '';
return (
<div className="Cover">
<div className="Cover-container">
<div>
<Avatar
username= {user}
profession="Web Developer"
location="New York, New York"
status="I am here to protect my business, a bunch of kids are out to ruin me" />
<div className="Cover-submenu-container">
<div className="Cover-submenu-section">
.
</div>
<div className="Cover-submenu-section links">
<a href="#" className="Cover-submenu-link">
<i className="fa fa-twitter"></i>
</a>
<a href="#" className="Cover-submenu-link">
<i className="fa fa-facebook"></i>
</a>
</div>
<div className="Cover-submenu-section connect-menu">
<Button className={classnames('follow-btn', {followClass})} href="#" onClick={this.toggleFollow.bind(this)}>Follow</Button>
<Button className="connect-btn" href="#" onClick={this.followBtn.bind(this)}>Connect</Button>
<Button className="follow-btn" href="#" onClick={this.followBtn.bind(this)}>Follow</Button>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default Cover;
I could not figure out what I am doing wrong here, I am quite new to reactJS. Any idea anybody? Thanks a lot.
The first thing you need to do is to add the initial value of the isFollowing property. Because you are using ES6 syntax, it's possible to do that in the constructor. Just add this code before toggleFollow() function:
constructor(props) {
super(props);
this.state = {
isFollowing: false
}
}
The second error (based on the comments at your question) comes from not having the function followBtn() defined. Add this before render() function:
followBtn() {
alert('followBtn called'); //change it for whatever you want
}
Don't forget that clicking on both buttons (connect, follow) will now lead to the same result, because the same function will be called.

Categories