I am trying to figure out how to conditionally render a partial in react. My condition is that when a list item is active, the partial should be rendered.
I am using bootstrap, which has an active element available for use.
My goal is to achieve something similar to intercom's terms page: https://www.intercom.com/terms-and-policies#billing
In my main menu, I have:
import React from 'react';
import ReactDOM from 'react-dom';
var Terms = require('../overview/terms.jsx');
var Privacy = require('../overview/privacy.jsx');
var Cookies = require('../overview/cookies.jsx');
var Thirdparty = require('../overview/thirdparty.jsx');
var Website = require('../overview/website.jsx');
var Sla = require('../overview/sla.jsx');
var Acceptable = require('../overview/acceptable.jsx');
var Billing = require('../overview/billing.jsx');
var Information = require('../overview/information.jsx');
var Incident = require('../overview/incident.jsx');
var Bug = require('../overview/bug.jsx');
class Menu extends React.Component {
render() {
return (
<div>
<div className = "row">
<div className = "col-md-8 col-md-offset-2 generalhead">
Terms and Policies
</div>
</div>
<div className = "row">
<div className = "col-md-3 col-md-offset-1">
<ul className="list-group">
Terms of Service
Privacy Policy
Cookies
Third Party Processors
Website Use
Service Level Agreement
Acceptable Use
Billing
Information Security
Incident Response Plan
Bug Bounty
</ul>
</div>
<div className = "col-md-6 col-md-offset-1">
<Terms />
<Privacy />
<Cookies />
<Thirdparty />
<Website />
<Sla />
<Acceptable />
<Billing />
<Information />
<Incident />
<Bug />
</div>
</div>
</div>
);
}
}
module.exports = Menu;
Each list item has a named data attribute. When that attribute is the active item, I want to then render the partial. At the moment all of them are rendering.
I have read the react docs on conditional rendering: https://facebook.github.io/react/docs/conditional-rendering.html
I was trying to define a const called active that I thought might make use of the bootstrap class - but that isn't working. I must have the wrong end of the stick for how to do this. I seems like a simple hide show for a rails js toggle. I can't figure out how to get started on tackling this in react.
Can anyone give me a steer in the right direction?
Keep track of which list item is active in your state
<a href="#" onClick={() => this.setState({active: 'terms'})}>Terms of Service</a>
Then conditionally only render the active item
{this.state.active == 'terms' && <Terms />}
But I suggest using react router instead since each item seems to correspond to a page.
Edit
You have to add a constructor to your class to make it stateful
class Menu extends React.Component {
constructor(props) {
super(props);
this.state = {
active: false
};
}
.
.
.
Related
I am working on a tasklist page. The main page has a form in which the user would add the task and click on the add task button and the corresponding task would be appended to the "todo-task" div. I am using my own created react element (Do laundry is just for example) which returns a checkbox followed by the text passed as prop. In the following code I am trying to append at the end of our todo-task div but it is showing me some error. Is my approach right? If not what approach should I take?
import React,{useState} from 'react'
import './Tasklist.css'
import Button from 'react-bootstrap/Button';
import AddBoxIcon from '#material-ui/icons/AddBox';
import Checkbox from '#material-ui/core/Checkbox';
import Task from './Task.js';
function Tasklist() {
const [task, settask] = useState("")
let pendingTask = document.getElementById('pending-task')
let doneTask = document.getElementById('done-task')
const addTask = (event)=>{
event.preventDefault()
let tmp_text=task.trim()
if(tmp_text==="")return
let task_tmp = document.createElement(<Task text={tmp_text} />);
pendingTask.appendChild(task_tmp);
settask("");
}
const keyDownEvent=(e)=>{
let key=e.key
if(key==='Enter' && !e.shiftKey)addTask(e);
}
return (
<>
<div className="add-task">
<form >
<textarea
type="text"
value={task}
rows={1}
placeholder="Add Task"
onChange={(e)=>settask(e.target.value)}
onKeyDown={keyDownEvent}
/>
<Button variant="outline-primary" onClick={addTask}>
<AddBoxIcon /> Add Task
</Button>
</form>
</div>
<div className="todo-task" id="pending-task">
</div>
<Task text="Enjjoyyyy!!!" />
<hr />
<div className="done-task" id = "done-task">
<ul id="done-task-list">
</ul>
</div>
</>
)
}
export default Tasklist
You're going about this in a fundamentally incorrect way, so it's not a quick fix. Now is the time to take a big step back and start over on your component.
What you're currently trying to do is modify the DOM directly. Don't do that in React. What you should instead be doing is maintaining state.
Instead of trying to directly create an element and add it to the DOM, create a record and update state with that record. This is an entirely separate task than rendering the DOM. The actual rendering is done based on that current state.
Currently in your state you have "a task". But you're trying to build functionality to add more tasks. So really your state should have a list of tasks, and you can add to that list. (Which can be in addition to the single task, of course. You can use useState as many times as you like.)
For example, consider state like this:
const [tasks, setTasks] = useState([]);
Then add a task in your button click handler:
const addTask = (event)=>{
event.preventDefault();
let tmp_text=task.trim();
if(tmp_text==="") return;
setTasks([...tasks, tmp_task]); // <--- here
settask("");
}
This keeps a running array of the tasks being stored.
Then in the rendering, you would .map over those tasks to show them:
<div className="todo-task" id="pending-task">
{tasks.map((t, i) => (
<Task key={i} text={t} />
))}
</div>
I am new to React and I have run into some difficulty I cannot seem to figure out.
I am using Lodash to remove a single object from an array, via an onClick eventHandler. However once I select to delete a specific item, the display is rendered as empty, However When I console.log the returned Array it shows the elements are present but wrapped in a lodashWrapper.
Also the element I selected for removal is still in the Array.
I am using the lodash method without and I have included all relevant snippets.
class App extends Component
{
constructor()
{
super();
this.state =
{
appointments : [],
idx : 0,
}
this.deleteAppointment = this.deleteAppointment.bind(this)
}
deleteAppointment(apt)
{
let temp = this.state.appointments;
console.log(temp);
temp = without(temp,apt)
console.log(temp);
this.setState({
appointments : temp
});
}
<ListAppointment appointments = {this.state.appointments}
deleteAppointment={this.deleteAppointment} />
class ListAppointment extends Component
{
render()
{
return (
<div className="appointment-list item-list mb-3">
{this.props.appointments.map(item =>(
<div className="pet-item col media py-3" key={item.aptID}>
<div className="mr-3">
<button className="pet-delete btn btn-sm btn-danger"
onClick={()=> this.props.deleteAppointment(item)} >
<FaTimes />
</button>
</div>
Any insight as to why this is occurring is greatly appreciated. I have tried different formats to Bind this but i still end with the same result, which is an empty array being rendered.
Thanks in Advance
I was learning React and I came to a point which created confusion. Everywhere I was using props while writing Function components.
I always use props.profile and it works fine. But in one code component, I had to write
const profiles=props; and it worked fine.
I tried using const profiles=props.profile; and also I tried using inside return in 'Card' function component
{props.profile.avatar_url} but both of them failed
Below is my code which works fine
const Card=(props)=>{
const profiles=props; //This I dont understand
return(
<div>
<div>
<img src={profiles.avatar_url} width="75px" alt="profile pic"/>
</div>
<div>
<div>{profiles.name}</div>
<div>{profiles.company}</div>
</div>
</div>
);
}
const CardList=(props)=>{
return(
<div>
{testDataArr.map(profile=><Card {...profile}/>)}
</div>
);
}
Can someone please help me understand why I can't use const profiles=props.profile?
What are the other ways to achieve the correct result?
Your testDataArr might be this,
testDataArr = [{avatar_url:"",name:"",company:""},{avatar_url:"",name:"",company:""},{avatar_url:"",name:"",company:""}]
Now when you do this,
{testDataArr.map(profile=><Card {...profile}/>)}
here profile = {avatar_url:"",name:"",company:""},
and when you do,
<Card {...profile}/>
is equivalent to,
<Card avatar_url="" name="" company=""/>
In child component, when you do this,
const profiles=props;
here props = {avatar_url:"",name:"",company:""}
So you can access it's values,
props.avatar_url
props.name
props.company
But when you do this,
const profiles=props.profile
profile key is not present in {avatar_url:"",name:"",company:""} object and it fails.
OK. Here is the issue, the props object does not contain a profile attribute, but IT IS the profile attribute. Becouse you are spreading the profile variable when you render the Card element (in the CardList), you basically are writing:
<Card avatarUrl={profile.avatarUrl} comapny={profile.comany} />
Instead, you should do
<Card profile={profile} />
and then in your Card component access the data this way
const Card = (props) => {
const profile = props.profile
}
or even simpler
const Card = ({profile}) => {
return <div>{profile.comany}</div>
}
I'm trying to develop a React program that changes information in a component each time the button "rectForward" or "rectBackward" is pressed. I'm passing the information in the form of an array of objects into my Body component from "importData" as seen below. That data is then converted into each object's indvidual data pieces through the map functions listed directly after render() is called. What I want to happen when the rectForward button is pressed is for the "text1" array value in Column1 to incrament by 1. The same happens when rectBackward is pressed, but I want the value to decrement. My primary difficulty is the syntax. As you can see in the statement onClick={Column1.text1=text1val[++], this was my first attempt at implementing this functionality, but the syntax is definitely incorrect. I was wondering if I could get some help formatting this
import React from "react";
import "./Body.css";
import Column1 from "./Body/Column1/Column1";
import "./Buttons.css";
import Column2 from "./Body/Column2/Column2";
import myData from "./myData";
class Body extends React.Component {
constructor() {
super()
this.state = {
importData: myData
}
}
render() {
var ID = this.state.importData.map(item => item.id)
var text1val = this.state.importData.map(item => item.text1)
var text2val = this.state.importData.map(item => item.text2)
var text3val = this.state.importData.map(item => item.text3)
return(
<div className="mainBody">
<div className="backPain">
<div className="holder">
<Column1 key={ID[0]} text1={text1val[0]}>
</Column1>
<div className="rectHolder">
<div className="rectForward" onClick={Column1.text1=text1val[++]}
<h2>Next</h2>
</div>
<div className="rectBackward">
<h2>Prev</h2>
</div>
</div>
<Column2 key={ID[0]} text2={text2val[0]} text3={text3val[0]}>
</Column2>
</div>
</div>
</div>
)
}
}
export default Body;
Thanks so much!
Simple thing i will do is keep an index in state. Than pass a function to next and prev which takes care of changing this index. and will show the values based on current state.
this.state = {
currentIndex : 0
}
HandleCurrentIndex(type){
if(type === 'inc'){
this.setState({currentIndex: this.state.currentIndex++})
} else {
this.setState({currentIndex: this.state.currnIndex-1 })
}
}
<div className="rectForward" onClick={()=>this.HandleCurrentIndex("inc")}>
<h2>Next</h2>
</div>
<div className="rectBackward" onClick={()=>this.HandleCurrentIndex('dec')}>
<h2>Prev</h2>
</div>
On side note:- This is just and example in product you should take care of index going below zero as well index exceeding limits of your data. which in turn will show undefined values. simple thing you should do is whenever it goes out of limit just reset it to default value ( 0 )
I have created a helper function that creates elements dynamically within my component when I click a button. However it's not displaying half of the html I'm trying to append to the parent div.
It adds the label correctly as html, but the rest is just in plain text. Can anyone see why?
The function used to dynamically create content:
function addElement(parentId, elementTag, elementId, html) {
let parentElement = document.getElementById(parentId);
let elementToAdd = document.createElement(elementTag);
elementToAdd.setAttribute('id', elementId);
elementToAdd.innerHTML = html;
parentElement.appendChild(elementToAdd);
}
My function within my component:
static addMatch() {
let html = "<div className=\"form-group\"><label className=\"control-label\">Add Match</label>" +
"<DatePickerselected={this.state.startDate}onChange={this.handleChange.bind(this)}/></div>";
addElement('fixture-parent', 'newMatch', uuid(), html);
}
My full react component is below:
import React, {Component} from "react";
import DatePicker from "react-datepicker";
import {addElement} from "../../helpers/DynamicElementsHelper";
import moment from "moment";
const uuid = require('uuid/v1');
require('react-datepicker/dist/react-datepicker.css');
class Fixtures extends Component {
constructor() {
super();
Fixtures.addMatch = Fixtures.addMatch.bind(this);
this.state = {
startDate: moment()
};
}
handleChange(date) {
this.setState({
startDate: date
});
}
static addMatch() {
let html = "<div className=\"form-group\"><label className=\"control-label\">Add Match</label>" +
"<DatePicker selected={this.state.startDate} onChange={this.handleChange.bind(this)} /></div>";
addElement('fixture-parent', 'newMatch', uuid(), html);
}
render() {
return (
<div className="tray tray-center">
<div className="row">
<div className="col-md-8">
<div className="panel mb25 mt5">
<div className="panel-heading">
<span className="panel-title">Fixtures</span>
<p>A list of fixtures currently on the system, pulled in via ajax from Ratpack</p>
</div>
<div className="panel-body p20 pb10">
<div id="fixture-parent" className="form-horizontal">
<div className="form-group">
<label className="control-label">Add Match</label>
<DatePicker
selected={this.state.startDate}
onChange={this.handleChange.bind(this)}/>
</div>
</div>
</div>
<button onClick={Fixtures.addMatch }>Add Match</button>
</div>
</div>
</div>
</div>
);
}
}
export default Fixtures;
In React, it is recommended to not interact with the DOM directly. Instead, you should modify the JSX depending on data that you have. For your code, instead of adding an HTML tag with data from the state that changes, you should change the state and display information based on that:
addMatch() {
//Add a start date to the list of starting dates
this.setState({
listOfStartDates: [...this.state.listOfStartDates, newDate]
});
}
render(){
//For each starting date, generate a new 'match' as you called them
// note: this is the same as the stringyfied HTML tag OP had in the question
let listOfMatches = this.state.listOfStartDates.map((date)=>{
return (
<div className="form-group">
<label className="control-label">
Add Match
</label>
<DatePicker selected={date} onChange={this.handleChange.bind(this)} />
</div>
);
/* then in here your returned JSX would be just as OP originally had, with the exception that you would have {listOfMatches} where OP used to have the inserted list of HTML tags */
return //...
}
Since the component will re-render every time the state changes, the component will always have as many matches as you have starting dates.
Hope that helps!
Put a space between DatePicker and selected.
"<DatePicker selected={this.state.startDate}onChange={this.handleChange.bind(this)}/></div>";