How to pass an object with onClick React? - arrays

I have an object in an external file and I want to pass the url into the button onClick but I do not know to pass the value.
Object:
const ProjectLists = [
{
id: "4",
iconImage: "",
name: "Simple",
description: "Simple is a corporate responsive template and the absolutely clean & modern template theme for your business. The theme was built by Foundation Framework and take advantages of it features: grid system, typography, buttons, form, UI element, section and more.",
technologies: "HTML, CSS, JavaScript",
href: "http://striped-dolls.surge.sh"
}
]
export default ProjectLists;
How to pass ProjectLists.map((project, i) => href in map() into <button>
class Projects extends Component {
render() {
return (
<div>
{ProjectLists.map((project, i) =>
<section className='section'>
<div className='row'>
<div className='col-sm'>
<div className='content-left'>
<p key={project.id + i}>
{project.iconImage}
</p>
</div>
</div>
<div className='col-sm-8'>
<div className='content-right text-left'>
<h1>{project.name}</h1>
<p>{project.description}</p>
<p>Technologies: {project.technologies}</p>
<button type='submit' onClick=() => </button>>View live</button>
</div>
</div>
</div>
</section>
)}
</div>
)
}
}
Thank you for your help!

You can pass extra data in your map using arrow functions:
{ProjectLists.map(project =>
<button onClick={onProjectClick(project)}>View live</button>
}
// e.g.
onProjectClick = project => event => {
console.log(project.description);
navigateTo(project.href);
}
I noticed your button has type="submit" so more the correct event to handle is onSubmit on the form element however as this handles pressing the Return key for example (it's not essential though).

You can use "a" tag for this purpose.
<a href={project.href}></a>
If you want to use onClick in your button, then you can try this.
onClick={() => { window.location.href = project.href }}

state = {
redirect: false
}
setRedirect = (href) => {
window.location.href = href;
}
Call the setRedirect function
<button onClick={()=>this.setRedirect(project.href)}>View live</button>

Related

Render JSX from material-ui Button onClick

I'm trying to render JSX when a material-ui button is clicked. I'm logging to the console when clicking but cannot see any of the JSX getting rendered.
interface TileProps {
address?: string;
}
const renderDisplayer = (address: string) => {
console.log('Rendering address', address!);
if (typeof(address) == 'undefined' || address == '') {
return(<div className='error'><li>No address found</li></div>)
}
return(<AddressDisplayer address={address} />)
}
const Tile = (props: TileProps) => {
return(
<div className='tile'>
<ul>
<li>{props.address}</li>
</ul>
<Button variant='contained' onClick={() => {renderDisplayer(props.address)}}>Display</Button>
</div>
)
}
export default Tile;
I can see the console.log('Rendering address', address!); running when the button is clicked, but the JSX isn't getting rendered.
Could this be because I'm using React functional components instead of class components?
Your question is somehow unclear for me. If you want to render <div className='error'><li>No address found</li></div> based on typeof(address) == 'undefined' || address == '' condition, there is no need to click on the button and it's better to use conditional rendering. For example:
{!props.address ? (
<div className='error'><li>No address found</li></div>
) : (
<AddressDisplayer address={props.address} />
)}
But if you want to render your address component by clicking on the button, you should define a state and set it true when clicking on the button. Like this:
const [shouldShowAddress, setShouldShowAddress] = useState(false);
{shouldShowAddress && (
<>
{!props.address ? (
<div className="error">
<li>No address found</li>
</div>
) : (
<AddressDisplayer address={props.address} />
)}
</>
)}
<Button
variant="contained"
onClick={() => {
setShouldShowAddress(true)
}}
>
Display
</Button>
Please read about the Life cycle This not how react work onclick function
renderDisplayer is called and return JSX to onClick event you need to use state here to render the component with ternary oprator renderDisplayer fuction do setState so DOM will update

react.js buttons that display different items from array

I didn't know how to ask this but basically I just want list of buttons that display different text. When you click a button it shows the text. But instead of hardcoding every button I want it to be dynamic. So far when I click any of my buttons, everything shows instead of individually. (one button should show one definition" I think it is the way I am using state probably.
import React, { useState } from "react";
import terms from "./TermsComponentData.js";
import "./TermsComponent.css";
function TermsComponent() {
const [showTerm, setShowTerm] = useState(false);
const buttonList = terms.map((term, index) => (
<div>
{showTerm && <h1>{term.definition}</h1>}
<button
key={index}
className="buttons-container-button"
onClick={() => {
setShowTerm(!showTerm);
}}
>
{term.name}
</button>
</div>
));
return (
<div className="terms-container">
<div className="buttons-container">{buttonList}</div>
</div>
);
}
MY DATA
const terms = [
{
name: "Term1",
definition: "This is the definition1",
},
{
name: "Term2",
definition: "This is the definition2",
},
{
name: "Term3",
definition: "This is the definition 3",
},
{
name: "Term4",
definition: "This is the definition 4",
}
];
export default terms;
Currently your state variable showTerm is common for all of the buttons. So to achieve the result you are describing, you need to encapsulate the state for each button.
The easiest way to do this, is to create a separate component which contains the showTerm state, and then map a list of this component with the given data based on the terms:
// 👇 added separate term button with it's own state
function TermButton({ term }) {
const [showTerm, setShowTerm] = useState(false);
return (
<div>
{showTerm && <h1>{term.definition}</h1>}
<button
className="buttons-container-button"
onClick={() => {
setShowTerm(!showTerm);
}}
>
{term.name}
</button>
</div>
);
}
function TermsComponent() {
return (
<div className="terms-container">
<div className="buttons-container">
{ // 👇 for each term, create a TermButton with the given term
terms.map((term, index) => <TermButton key={index} term={term} />)}
</div>
</div>
);
}

How to listen to localstorage value changes in react?

I want to show a button when user is logged.If user is not logged then I m not showing button.When user logged i will set local storage values.when i set local storage in login Component,Header component must listen to that event and show the button.I m using addEventListener for listening.But its not listening.
I don't know where to listen in header Component.
// HeaderComponent(header.js):
class HeaderComponent extends Component {
componentDidMount(){
if(typeof window!='undefined'){
console.log(localStorage.getItem("token"));
window.addEventListener("storage",function(e){
this.setState({ auth: true});
})
}
}
render() {
return (
<div className="header">
<div className="container">
<div className="header-content">
<img src={logo} alt="logo"></img>
<div className="nav-links" >
<ul >
<li>Home</li>
<li>About</li>
<li>Services</li>
<li><NavLink activeClassName="active" to="/upload" >Upload</NavLink></li>
<li><NavLink activeClassName="active" to="/signup"> Sign Up</NavLink></li>
{ this.state.auth? <li onClick={this.onLogout}>Logout</li> :null}
</ul>
</div>
</div>
</div>
</div>
);
}
}
//loginComponent(login.js)
class LoginComponent extends Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit(event) {
const data = {
username: document.getElementById('name').value,
password: document.getElementById('password').value
}
axios.post(`http://localhost:4000/user/login`, data).then(res => {
this.props.history.push("/");
localStorage.setItem("token",res.data.token);
localStorage.setItem("auth",true);
}).catch(err => console.log(err));
}
render() {
return (
<section class="log-in">
<div class="card-col">
<form>
<h3>LOG IN</h3>
<div class="form-controls">
<input id="name" type="text" placeholder="username" class="input"></input>
</div>
<div class="form-controls">
<input id="password" type="password" placeholder="password" class="input"></input>
</div>
<button type="submit" onClick={this.onSubmit} class="button" >Log in</button>
</form>
</div>
</section>
)
}
}
The current answers are overlooking a really simple and secure option: window.dispatchEvent.
Where you set your localStorage item, if you dispatch an event at the same time then the eventListener in the same browser tab (no need to open another or mess with state) will also pick it up:
const handleLocalStorage = () => {
window.localStorage.setItem("isThisInLocalStorage", "true");
window.dispatchEvent(new Event("storage"));
};
window.addEventListener('storage', () => {
console.log("Change to local storage!");
// ...
})
EDIT:
Because this seems to be helpful, I'd also recommended checking out the useLocalStorage hook from the usehooks-ts team. You don't need to install it as a package; you can just copy the hook wholesale. This hook makes use of the solution I originally shared, but adds a whole lot more sophisticated logic to it.
Please take note of two things
storage event works only when the same application opened in two browser tabs (it is used to exchange info between different tabs of the same app). Storage event will not fire when both components shown on the same page.
When adding event listerner, you're passing function(), not array function. function() doe not capture this so you should explicitly bind(this) or change it to arrow function.
For example
window.addEventListener("storage",(function(e){
this.setState({ auth: true});
}).bind(this));
Or do with arrow function
window.addEventListener("storage",(e) => {
this.setState({ auth: true});
});
Here is simple example.
Be sure to open it in two tabs (the same link). Store value in one tab and see this value in another tab.
I found a really bad hack to accomplish this:
I have a Toolbar and a Login Component where the Toolbar component listens to changes in localStorage and displays the logged-in user name when the Login Component updates local storage if authentication is successful.
The Toolbar Component
(similar to the Header component in your case)
const [loggedInName, setLoggedInName] = useState(null);
useEffect(() => {
console.log("Toolbar hi from useEffect")
setLoggedInName(localStorage.getItem('name') || null)
window.addEventListener('storage', storageEventHandler, false);
}, []);
function storageEventHandler() {
console.log("hi from storageEventHandler")
setLoggedInName(localStorage.getItem('name') || null)
}
function testFunc() {
console.log("hi from test function")
storageEventHandler();
}
Add a hidden button to your Toolbar component. This hidden button will call the testFunc() function when clicked which will update the logged-in user's name as soon as local storage is updated.
<button style={{ display: 'none' }} onClick={testFunc} id="hiddenBtn">Hidden Button</button>
Now, in your Login component
.
.
.
//login was successful, update local storage
localStorage.setItem("name",someName)
//now click the hidden button using Javascript
document.getElementById("hiddenBtn").click();
.

React router not rendering the component on click of a Array item

I'm creating a component which has array of items and showing them on one page using map function.
Need help on how I can show a detail view of a item by updating route value dynamically based on the item I clicked.
Currently when I click on a item know more though the url change to some wrong value I don't see any change in the page.
Component:
{location[area].map((item, index) =>
<div key={index} className="col-md-4 mt-3">
<div className="card">
<div className="card-body">
<h5 className="card-title">{item.title}</h5>
<p className="card-text">{item.description}</p>
<Link to={'/view/${item.title}'} onClick={() => addDetail(item)} className="btn btn-primary">Know more</Link>
</div>
</div>
</div>
)}
Router:
<Route path='/view/:id' exact component={DetailPage} />
DetailPage Component:
class DetailPage extends Component {
render() {
const selectedItem = this.props.selectedItem;
return(
<div>
<DetailedView selectedItem={this.props.selectedItem} />
</div>
);
}
}
Result Anchor Tag:
<a class="btn btn-primary" href="/view/${item.title}">Know more</a>
Onclick result url:
http://localhost:3000/view/$%7Bitem.title%7D
You need to use backticks for the to prop of your Link components so that template literals will be used and the variable will be inserted into the string.
<Link
to={`/view/${item.title}`}
onClick={() => addDetail(item)}
className="btn btn-primary"
>
Know more
</Link>
Well,As #tholle suggested use template literal.Your route seems fine, just make use of react life cycle method componentWillReceiveProps in the Detail Page Component.Here is the code
class DetailPage extends Component {
componentWillReceiveProps(nextProps){
if(this.props.match.params.id !== nextProps.match.params.id){
//make a call with updated nextProps.match.id or filter the existing data store
with nextProps.match.id
}
}
render() {
const selectedItem = this.props.selectedItem;
return(
<div>
<DetailedView selectedItem={this.props.selectedItem} />
</div>
);
}
}

Ckeditor disable auto inline won't disable inline from being selected on page load

I'm trying to develop a simple CMS for my page. I want it to where I can edit and delete a users reply on click of a button. I got the delete functionality done so I figured for the reply functionality I would use CKeditor. What I'm struggling with is not being able to disable the autoinline feature. I can still select my div on load of the page rather than clicking a button to enable the inline feature but I don't know what I am doing wrong?
I have tried setting the feature directly in my index.html file, a custom js script file and the config.js ckeditor file but none worked. I am using Ckeditor 4.
this is the snippit of code I'm trying to use to disable inline on my div element but it's not working and I don't know why, i currently have it placed in a custom.js script file and I'm calling it from my index.html file
CKEDITOR.disableAutoInline = true;
Here is my code for my replies page:
import React from 'react';
import CKEditor from 'react-ckeditor-component';
import ForumpageService from '../../services/forumService';
import appController from '../../controllers/appController';
class Forumreplies extends React.Component {
constructor(props){
super(props);
this.elementName = "editor_" + this.props.id;
this.editReply = this.editReply.bind(this);
this.state = {
reply: '',
errorMsg: '',
isLoggedin: false,
// Ck Editor State
reply: '',
}
}
async componentDidMount(){
const topicId = this.props.match.params.topicid
const postDetails = await ForumpageService.replyDetails({topicId: topicId})
this.setState({
postDetails: postDetails[0],
topicId: topicId
})
await this.postReplies();
}
// Edit the reply
async editReply(id, e){
//CKEDITOR.inline(this.elementName);
}
async postReplies(){
const repliesData = await ForumpageService.postreplyDetails({topicId: this.state.topicId})
await this.setState({repliesData});
}
render(){
const repliesData = currentReply.map((row, index) => {
return (
<div className="row" id="reply-messages" key={index}>
<div className="col-md-8" suppressContentEditableWarning contentEditable="true" id={this.elementName}>
<p dangerouslySetInnerHTML={{__html: row.reply_message }} />
</div>
<div className="col-md-2">
{this.state.isloggedinuserId == row.reply_user_id && this.state.isLoggedin == true ? <i className="far fa-edit" onClick={this.editReply.bind(this, row.reply_id)} title="Edit this reply?"></i> : null }
</div>
</div>
})
return (
<div className="container" id="forum-replies">
<div className="row">
{repliesData}
</div>
</div>
)
}
}
export default Forumreplies;
Instead of creating a div and calling CKEDITOR.inline, you should try to use the react-ckeditor-component as its own way (not as an inline editor).
You could do something like: if the button wasn't pressed, render a div with the text content, but after the button was pressed, render a <CKEditor /> component as in the documentation
There is no documented way to set the editor as inline in this package that you are using.
EDIT:
I can see you are not using the react-ckeditor-component features, but following what you've done so far, you could set the contentEditable="true" property of the div only when the button is pressed:
<div className="col-md-8" suppressContentEditableWarning contentEditable={this.state.isEditing} id={this.elementName}>
<p dangerouslySetInnerHTML={{__html: row.reply_message }} />
</div>
And then set the isEditing to true on the onClick handler. Your component will update and then re-render with the contentEditable property set to true

Resources