Within my React app, I have a sidebar which needs to have a CSS class added to it when the sidebar close button is clicked. I'm using React.createRef() to create a reference to the element, however, I'm receiving the following error:
Here's my code:
import React from 'react';
import './css/Dashboard.css';
class Dashboard extends React.Component {
constructor(props) {
super(props);
this.sidebar = React.createRef();
}
sidebarClose() {
console.log('test');
this.sidebar.className += "hidden";
}
render() {
return (
<div id="dashboard">
<div ref={this.sidebar} id="sidebar">
<img width="191px" height="41px" src="logo.png"/>
<div onClick={this.sidebarClose} className="sidebar-close">X</div>
</div>
</div>
);
}
}
export default Dashboard;
The console.log('test') is just so that I can confirm the function is being executed (which it is).
Thank you.
Instead of manually trying to add a class to a DOM node, you can keep a variable in your state indicating if the sidebar is open and change the value of that when the button is clicked.
You can then use this state variable to decide if the sidebar should be given the hidden class or not.
Example
class Dashboard extends React.Component {
state = { isSidebarOpen: true };
sidebarClose = () => {
this.setState({ isSidebarOpen: false });
};
render() {
const { isSidebarOpen } = this.state;
return (
<div id="dashboard">
<div
ref={this.sidebar}
id="sidebar"
className={isSidebarOpen ? "" : "hidden"}
>
<img
width="191px"
height="41px"
src="logo.png"
alt="craftingly-logo"
/>
<div onClick={this.sidebarClose} className="sidebar-close">
X
</div>
</div>
</div>
);
}
}
I think you forget to bind sidebarClose method to your class in constructor.
constructor(props) {
super(props);
this.sidebar = React.createRef();
this.sidebarClose = this.sidebarClose.bind(this); // here
}
Related
I know there were similar questions but non of them answered my question.
To visualize the problem here is what it looks like.
Ok, so i have the following problem:
After i click Left component i want to change the Body to BodyLeftClicked,
after i click Right component i want to change the Body to BodyRightClicked.
I have App component sth like this:
class App extends React.Component {
state = { choose: "Left" }; //initialize one body
render() {
return (
<div className="All">
<div className="head">
<Top name={"Left"}/>
<Top name={"Right"}/>
</div>
<div className="body">
{(() => {
switch(this.state.choose) {
case "Left":
return <BodyLeftClicked choose={this.state.choose}/>;
case "Right":
return <BodyRightClicked choose={this.state.choose}/>;
}
})()}
</div>
</div>
);
}
}
Two components at the top of the page (Left and Right components) are made by:
class Top extends React.Component {
constructor(props) {
super(props);
this.state = { name : this.props.name };
this.onButtonClick = this.onButtonClick.bind(this);
}
onButtonClick() {
//CHANGE THE BODY WHICH IS BEING SHOWN IN APP
}
render() {
return (
<div>
<button className="Button" type="button" onClick={this.onButtonClick}>{this.state.name}</button>
</div>
)
}
}
And example of body Component:
class BodyLeftClicked extends React.Component {
constructor(props) {
super(props);
this.state = { choose : this.props.choose };
}
render() {
return (
<div className="BodyLeftClicked"> </div>
)
}
}
You'd basically want to create a method in the App class and pass it down to your Left/Right components. In the method use setState and it would change according to the argument passed to it. It would look something like this:
class App extends React.Component {
state = { choose: "Left" };
setChoose = position => {
this.setState({ choose: position });
}
}
Then pass it down as so:
<Top name="Left" onButtonClick={ this.setChoose } />
and call it from within the component through a click handler:
class Top extends React.Component {
render() {
return (
<div>
<button className="Button" type="button" onClick={props.onButtonClick}>{this.state.name}</button>
</div>
);
}
}
Ideally you'd do the same for the right component.
Using Hooks
Pretty much the same thing but you would use, useState and get the setter method for free essentially:
function App(props) {
const [choose, setChoose] = useState('Left');
return (
<Top name="Left" onButtonClick={ setChoose } />
);
}
I have a React.Component with render() declared this way:
render(){
return <div>
<button id="butt" onClick={()=> $("#noti").change("test") }>click me</button>
<Notification id="noti" onMounted={() => console.log("test")}/>
</div>
}
And this is my Notification class:
class Notification extends React.Component {
constructor(props) {
super(props)
this.state = {
message: "place holder",
visible: false
}
}
show(message, duration){
console.log("show")
this.setState({visible: true, message})
setTimeout(() => {
this.setState({visible: false})
}, duration)
}
change(message){
this.setState({message})
}
render() {
const {visible, message} = this.state
return <div>
{visible ? message : ""}
</div>
}
}
As the class name suggests, I am trying to create a simple notification with message. And I want to simply display the notification by calling noti.show(message, duration).
However, when I try to find noti by doing window.noti, $("#noti") and document.findElementById("noti"), they all give me undefined, while noti is displayed properly. And I can find the butt using the code to find noti.
How should I find the noti? I am new to front end so please be a little bit more specific on explaining.
It's not a good idea using JQuery library with Reactjs. instead you can find a appropriate react library for notification or anything else.
Also In React we use ref to to access DOM nodes.
Something like this:
constructor(props) {
super(props);
this.noti = React.createRef();
}
...
<Notification ref={this.noti} onMounted={() => console.log("test")}/>
more info: https://reactjs.org/docs/refs-and-the-dom.html
I have hardcoded the id to 'noti' in the render method. You can also use the prop id in the Notification component.I have remodelled the component so that you can achieve the intended functionality through React way.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
messageContent: 'placeholder'
}
}
setMessage = (data) => {
this.setState({messageContent : data});
}
render() {
return (
<div className="App">
<button id='butt' onClick= {() => this.setMessage('test')} />
<Notification message = {this.state.messageContent} />
</div>
);
}
}
class Notification extends React.Component {
render () {
const {message} = this.props;
return (
<div id='noti'>
{message}
</div>
)
}
}
Before beginning: Using id/class to reach DOM nodes is not suggested in React.js, you need to use Ref's. Read more at here.
In your first render method, you give id property to Notification component.
In react.js,
if you pass a property to some component, it becomes a props of that
component. (read more here)
After you give the id to Notification, you need to take and use that specific props in your Notification component.
You see that you inserted a code line super(props) in constructor of Notification? That means, take all the props from super (upper) class and inherit them in this class.
Since id is HTML tag, you can use it like:
class Notification extends React.Component {
constructor(props) {
// inherit all props from upper class
super(props);
this.state = {
message: "place holder",
visible: false,
// you can reach all props with using this.props
// we took id props and assign it to some property in component state
id: this.props.id
}
}
show(message, duration){
// code..
}
change(message){
// code..
}
render() {
const {visible, message, id} = this.state
// give that id to div tag
return <div id={id}>
{message}
</div>
}
}
You can't pass id/class to a React Component as you would declare them in your normal HTML. any property when passed to a React Component becomes a props of that component which you have to use in the component class/function.
render() {
const {visible, message} = this.state
// give your id props to div tag as id attr
return <div id={this.props.id}>
{message}
</div>
}
This answer does not provide the exact answer about selecting a component as you want. I'm providing this answer so you can see other alternatives (more React way maybe) and improve it according to your needs.
class App extends React.Component {
state = {
isNotiVisible: false
};
handleClick = () => this.setState({ isNotiVisible: true });
render() {
return (
<div>
<button onClick={this.handleClick}>Show Noti</button>
{this.state.isNotiVisible && (
<Noti duration={2000} message="This is a simple notification." />
)}
</div>
);
}
}
class Noti extends React.Component {
state = {
visible: true
};
componentDidMount() {
setTimeout(() => this.setState({ visible: false }), this.props.duration);
}
render() {
return this.state.visible && <div>{this.props.message}</div>;
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root" />
So I know this question has been asked a couple of times and the general concession is that props cant be changed when it has already passed down to a child. The situation I have here is that basically i have a different onClick function in a different file that updates the the id="movie-header" with an innerHTML, the DOMSubtreeModified and componentDidUpdatedetects the change and pass down the new props to Child "Ebay".
So the question here is how do I get the Ebay component to update its state and make use of the new value with every change to the state in the moviemodalwindow(the parent of the Ebay)
MovieModalWindow.js
import React from "react";
import "../MovieGo.css";
import Ebay from "../Store/Ebay";
class MovieModalWindow extends React.Component {
constructor() {
super();
this.state = {
name: 1
};
}
componentDidMount() {
var element = document.getElementById("movie-header");
element.addEventListener("DOMSubtreeModified", this.myFunction(element));
var name = this.state.name + 1;
this.setState({ name: [...this.state.name, name] });
}
myFunction = input => event => {
this.setState({ name: input.innerHTML });
};
componentDidUpdate(prevProps, prevState) {
if (prevState.name != this.state.name) {
window.localStorage.setItem("keyword", this.state.name);
}
}
render() {
return (
<div id="myModal" class="modal">
<div class="modal-content">
<span onClick={onClose} class="close">
×
</span>
<h1 id="movie-header" />
<div className="middle-window">
<div className="left">
<Ebay id="ebay" keyword={this.state.name} />
</div>
</div>
<h3>PLOT</h3>
<p id="moviedetails" />
</div>
</div>
);
}
}
export default MovieModalWindow;
Ebay.js File
import React from "react"
class Ebay extends React.Component{
constructor(){
super();
this.state={
data:[],
}
}
componentWillUpdate(prevProps, prevState){
if (prevProps.keywords!=this.props.keywords){
console.log(window.localStorage.getItem("keyword"))
}
render(){
const{newInput} =this.props
return(
<div>
</div>
)
}
}
export default Ebay
I'm unsure if I'm answering the question you're asking, so apologies if this isn't what you're asking.
Step 1. Make Ebay's prop's change when you need this update to happen. (I think you stated you already have this occurring?)
Step 2: Make Ebay's state update when the props change. Here you can just watch for prop changes with componentWillReceiveProps and update the state accordingly.
class Ebay extends React.Component {
constructor() {
super();
this.state = { data: [] };
}
componentWillRecieveProps(nextProps) {
if (nextProps.keyword !== this.props.keyword) {
this.setState({ data: ['something new'] });
}
}
render() { ... }
}
I'm rank new to React and to Javascript in general. Thus the question.
I've a list of images being displayed in a React component. What I'm trying to achieve is to define a method to handle the onClick method on any of these images and render a new component as an overlay. This is my code:
class Viewer extends React.Component{
constructor(props){
super(props);
this.state = {
images : ImageList
};
}
componentDidMount(){
}
handleClick(mediaId, event){
event.preventDefault();
render(){
<MyNewComponent mediaId=mediaId />
}
}
render(){
return (
<div className="row">
<div className="image-viewer">
<ul className="list-inline">
{this.state.images.map(function (image) {
return (<li key={image.mediaId}><a href="#" onClick={this.handleClick.bind(image.mediaId, event)}><img src={image.src}
className="img-responsive img-rounded img-thumbnail"
alt={image.mediaId}/></a></li>);
})}
</ul>
</div>
</div>
);
}
}
export default Viewer;
This clearly is wrong and throws up a bunch of errors. Can someone point me in the right direction?
Here how to handle the click event and show the overlay
class Viewer extends React.Component{
constructor(props){
super(props);
this.state = {
images : ImageList,
mediaId : 0
};
// Bind `this`
// See "Binding to methods of React class" link below
this.handleClick = this.handleClick.bind(this);
}
componentDidMount(){ }
handleClick(event){
event.preventDefault();
// Get the value of the `data-id` attribute <a data-id="XX">
var mediaId = event.currentTarget.attributes['data-id'].value;
// Set the state to re render
this.setState({ mediaId }); // ES6 shortcut
// this.setState({ mediaId: mediaId }); // Without shortcut
}
render(){
// Set a variable which contains your newComponent or null
// See "Ternary operator" link below
// See "Why use null expression" link below
var overlay = this.state.mediaId ? <MyNewComponent mediaId={this.state.mediaId} /> : null;
return (
<div className="row">
<div className="image-viewer">
{ overlay }
<ul className="list-inline">
{this.state.images.map(image => {
return (<li key={image.mediaId}><a href="#" onClick={this.handleClick} data-id={image.mediaId}><img src={image.src}
className="img-responsive img-rounded img-thumbnail"
alt={image.mediaId}/></a></li>);
}).bind(this)}
</ul>
</div>
</div>
);
}
}
export default Viewer;
Documentation
React and ES6 - Binding to methods of React class
Ternary operator
Why use null expression
When I use React+Redux+Immutable, I get an issue: the component created by dynamic way, when the props change, component not rerender.
Is it React bug?
I deleted business code, just React code here: http://codepen.io/anon/pen/GoMOEZ
or below:
import React from 'react'
import ReactDOM from 'react-dom'
class A extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'tom'
}
}
dynamic() {
ReactDOM.render(<B name={this.state.name} changeName={this.changeName.bind(this)} type={false}/>, document.getElementById('box'))
}
changeName() {
this.setState({
name: 'tom->' + Date.now()
});
}
render() {
return <div>
top name: {this.state.name}
<B name={this.state.name} changeName={this.changeName.bind(this)} type={true}/>
<div id="box"></div>
<button onClick={this.dynamic.bind(this)}>dynamic add component</button>
</div>
}
}
class B extends React.Component {
render() {
return <div>
{this.props.type ? '(A)as sub component' : '(B)create by ReactDOM.render'}
- name:【{this.props.name}】
<button onClick={this.props.changeName}>change name</button>
</div>
}
}
ReactDOM.render(
<A/>,
document.getElementById('example')
);
It is not a bug, it is just not React-way to do what you want. Each call to A.render will overwrite <div id="box">...</div> deleting elements added by A.dynamic.
More idiomatic way is to add some flag, set it in onClick handler and use it in A.render to decide if <div id="box"> should be empty or not.
See edited code on codepen: http://codepen.io/anon/pen/obGodN
Relevant parts are here:
class A extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'tom',
showB: false // new flag
}
}
changeName() {
this.setState({
name: 'tom->' + Date.now()
});
}
// changing flag on button click
showB() {
this.setState({showB: true})
}
render() {
// `dynamic` will contain component B after button is clicked
var dynamic;
if(this.state.showB) {
dynamic = <B
name = {this.state.name}
changeName = {this.changeName.bind(this)}
type = {false} />
}
return <div>
top name: {this.state.name}
<B name = {this.state.name}
changeName = {this.changeName.bind(this)}
type = {true}/>
<div>{dynamic}</div>
<button onClick = {this.showB.bind(this)}>
dynamic add component
</button>
</div>
}
}
Update
You can still use your approach, but you need to manually update dynamically created component.
E.g. you can manually rerender it in changeName function.
changeName() {
this.setState({
name: 'tom->' + Date.now()
}, this.dynamic.bind(this));
}
Note, that this.dynamic is passed as a second argument to this.setState this ensures that it will be called when state is really updated. Just adding this.dynamic() after this.setState({...}) will use not-yet-updated state.
Codepen here: http://codepen.io/anon/pen/EPwovV