I'm fairly new at react.js, so any help is greatly appreciated.
I have this: https://jsfiddle.net/rzjyhf91/
Wherein I have made 2 components: an image and a button.
The goal is to remove the image with a click of the button, I use unmountComponentAtNode for that, but it does not work:
var App = React.createClass({
render: function() {
return (
<div><MyImage /><RemoveImageButton /></div>
);
}
});
var MyImage = React.createClass({
render: function() {
return (
<img id="kitten" src={'http://placekitten.com/g/200/300'} />
);
}
});
var RemoveImageButton = React.createClass ({
render: function() {
return (
<button onClick={this.handleClick}>remove image</button>
)
},
handleClick: function(){
React.unmountComponentAtNode(document.getElementById('kitten'));
}
});
React.render(<App />, document.body);
How can I remove a react component from another component?
Well, it seems you should rethink how the display control is handled. React is all about isolated components, and so, you shouldn't be unmounting a component that is mounted by a parent component. Instead, you should use a callback passed down through props to accomplish something like that.
Your actual implementation will depend on your use case, but an updated version of your example that works is at: https://jsfiddle.net/nt99zzmp/1/
var App = React.createClass({
render: function() {
var img = this.state.showImage ? <MyImage /> : '';
return (
<div>{img}<RemoveImageButton clickHandler={this.removeImage} /></div>
);
},
getInitialState: function() {
return {
showImage: true
};
},
removeImage: function() {
this.setState({ showImage: false });
}
});
var MyImage = React.createClass({
render: function() {
return (
<img id="kitten" src={'http://placekitten.com/g/200/300'} />
);
}
});
var RemoveImageButton = React.createClass ({
render: function() {
return (
<button onClick={this.props.clickHandler}>remove image</button>
)
}
});
React.render(<App />, document.body);
Basically removing a component doesn't make sense in React, you probably still thinking jQuery ways, basically in all modern and new JavaScript libraries including React, you should manage your component using state or a route to handle these things, deleting an element or component is not a good way to do these things in React or Angular for example.
For example you can have a boolean in this case and if it's true, show your image, otherwise hide it, or even return a different element in your component.
So in this case, you have a component which will return differently depends on props or state... something like this:
////
var MyImage = React.createClass({
render: function() {
if(this.state.showImage) {
return (
<img id="kitten" src={'http://placekitten.com/g/200/300'} />
);
} else {
return<p>no image!</p>;
}
}
});
////
In this example, if you set this.state.render = false, the component will be removed from DOM:
render() {
const { render } = this.state;
if (render === false) return null;
return (<p>I am here as long as render isn't false</p>);
}
Related
I'm trying to create a component that change when someone hover over it and let a img appear but I don't know whats wrong with my code, I'm not getting any error in the console but the img isn't showing. Here's my code:
var React = require('react');
var ReactDOM = require('react-dom');
var Troll = {
title: 'Why so serious?',
src: '../imagenes/troll.png'
};
var Common = React.createClass({
handleHover: function() {
return (
<img src={Troll.src} id='cara'/>
);
},
render: function () {
return (
<div onMouseEnter={this.handleHover}>
<h2>{Troll.title}</h2>
</div>
);
}
});
ReactDOM.render(
<Common />,
document.getElementById('app')
);
You can only return JSX in a render function, but you are trying to do it in an event. Your handleHover method should contain just the logic to replace the markup that is being rendered, not the markup itself.
The function handleHover must not return anything. It just need to show/hide the image.
Check this code:
class Avatar {
render () {
return (
<div
className="avatar"
onMouseOver={this.showImage.bind(this)}
onMouseLeave={this.hideImage.bind(this)}>
<figure ref="image">
<img src={this.props.img}/>
</figure>
<p>John Doe</p>
</div>
)
}
showImage () {
this.refs.image.classList.add('visible');
}
hideImage () {
this.refs.image.classList.remove('visible');
}
}
const node = document.getElementById('comp');
ReactDOM.render(
<Avatar
img="http://www.material-ui.com/images/uxceo-128.jpg"
/>,
node
);
We are just binding methods to mouseover and mouseleave events. The first method just show the image adding a CSS class, and the other method remove this class to hide it.
Click here to see this code running.
As mentioned in other answers, you don't need to return anything. Try using React state to solve this instead:
var React = require('react');
var ReactDOM = require('react-dom');
var Troll = {
title: 'Why so serious?',
src: '../imagenes/troll.png'
};
var Common = React.createClass({
getInitialState: function() {
return {
showTrollImage: false
}
},
handleHover: function() {
this.setState({ showTrollImage: true });
},
render: function () {
var trollImage = null;
if (this.state.showTrollImage) {
trollImage = <div><img src={Troll.src} id="cara" /></div>;
}
return (
<div onMouseEnter={this.handleHover}>
<h2>{Troll.title}</h2>
{trollImage}
</div>
);
}
});
ReactDOM.render(
<Common />,
document.getElementById('app')
);
And BTW, try to update your code to use ES6. See here for an example of what modern React code looks like. I'll convert your Common component:
class Common extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
showTrollImage: false,
};
}
handleHover = () => {
this.setState({ showTrollImage: true });
};
render() {
let trollImage = null;
if (this.state.showTrollImage) {
trollImage = <div><img src={Troll.src} id="cara" /></div>;
}
return (
<div onMouseEnter={this.handleHover}>
<h2>{Troll.title}</h2>
{trollImage}
</div>
);
}
});
Note that this syntax:
handleHover = () => { };
is Babel class property transformation, and you'll need to install Babel stage 2 support in your project to use this.
This is part of the component :
import MyComp from '../../lib/MyComp'
const Data = ( { data } ) => (
<div className="data-box" id="data-box">
<MyComp data={data} />
</div>
)
How do I get the width of the data-box div inside MyComp container?
Check this working demo: JSFiddle:
var Parent = React.createClass({
render: function() {
return <div id="parent">Hello Parent<Child></Child></div>;
}
});
var Child = React.createClass({
componentDidMount: function() {
alert('Parent width: ' + this.refs.child.parentNode.clientWidth);
},
render: function() {
return <div ref="child">Hello Child</div>;
}
});
Stating ref="child" will make the element accessable by the component itself, through this.refs.child. It is the vallina node instance. Using this.refs.child.parentNode.clientWidth will return the parent's width. Or, use this.refs.child.parentNode.getBoundingClientRect().
Reference: React refs
You need to use react refs.
on your MyComp class:
class MyComp extends React.Component {
//All your PropTypes and functions...
//New function
newFunction () {
console.log(this.refs.refName);
//This will give you the Data component. There you can call methods to calculate width, or whatever you need to do with that component
}
//Your render function
render() {
return <div ...whatever you have... ref="refName">
}
}
You can check react documentation
what should work is something like this
MyComp could look like this
render() {
return <div ref="node"></div>
}
with this.refs.node you get the current dom element and with
this.res.node.parentNode
you should get the parentNode
I'm learning ReactJS and need to pass a variable inside the same component.
Here's an example
var DataBase = [
{
position: 1
},
{
position: 2
},
{
position: 3
},
{
position: 4
}
];
var Component = React.createClass({
getDefaultProps: function() {
var counter = 0;
},
componentDidMount: function() {
var dbPos = this.props.db[counter+1].position;
return dbPos;
},
render: function () {
return (
<div className="Component">
{this.dbPos}
</div>
);
}
});
ReactDOM.render(
<Component db={DataBase} />,
document.getElementById('main')
);
So, this obviously doesn't work. What I need is to pass var dbPos created in componentDidMount to the render (without any events like onClick). This will be time driven, like 10 seconds in each position with setTimeout().
Is this possible? How? Is there a better solution? I'll appreciate all your help.
That problem may regard state handling. There are multiple ways to handle the application's state in a React application, but I will assume that you are interested in keeping dbPos as part of the component's state (and that you may be mutating it in the future). To achieve this, simply use this.setState and this.state.
Before I show the example, I will state a few other mistakes in your code snippet:
getDefaultProps should return a hash object of the props, not declare them with var (that would make them scoped to the method rather than the component instance)
counter, as a prop, must be referred as this.props.counter. Note that counter is not part of this component's state, and can only change with a respective change of that prop in an upper level of the component tree.
With that in mind:
var Component = React.createClass({
getDefaultProps: function() {
return {counter: 0};
},
componentDidMount: function() {
var dbPos = this.props.db[this.props.counter+1].position;
this.setState({ // change the state of this component
dbPos: dbPos
})
},
render: function () {
return (
<div className="Component">
{this.state.dbPos}
</div>
);
}
});
If you do not want dbPos to mutate as part of the component's state, simply make a new method for retrieving the intended position. No mutable state will be involved here.
var Component = React.createClass({
getDefaultProps() {
return {counter: 0};
},
componentDidMount() {
// no longer needed
},
getPosition() {
return this.props.db[this.props.counter + 1].position;
},
render () {
return (
<div className="Component">
{this.getPosition()}
</div>
);
}
});
I want to call a function of a child component.
Is there a possibility to get refs from this.props.children in React.
var ComponentSection = React.createClass({
componentDidMount: function() {
// How to access refs in this.props.children ?
this.refs.inner.refs.specificPanel.resize();
},
render: function () {
return (
<div className="component-section" ref="inner">
{this.props.children}
</div>
);
}
});
var Panel = React.createClass({
resize: function() {
console.log('Panel resizing');
},
render: function () {
return (
<div className="Panel">
Lorem ipsum dolor sit amet
</div>
);
}
});
var MainComponent = React.createClass({
render: function () {
return (
<ComponentSection>
<Panel ref="specificPanel"></Panel>
</ComponentSection>
);
}
});
ReactDOM.render(
<MainComponent></MainComponent>,
document.getElementById('container')
);
I made a little demo: https://jsfiddle.net/69z2wepo/26929/
Thanks in advance
Ref in React is a relationship between a component and its owner while what you want is a relationship between an element and its parent. A parent-child relationship is opaque, the parent only receives the child elements on render and never gets access to the resolved components.
In your case, the ref="specificPanel" establishes a link from the MainComponent to the Panel. If you want to call methods of Panel from ComponentSection, it should own the Panels instead of receiving them as children.
You could always get creative with React.Children.map and React.createElement (cloning the elements by hand and thus stealing them from the original owner), but this introduces some serious potential pitfalls. A better approach would likely be re-thinking the ownership structure of your component tree.
You are trying to set a ref on a <div> instead of a React component.
You could also refactor your code so that only <ComponentSection> needs to know about the <Panel> component, and render it in it's render function.
var ComponentSection = React.createClass({
componentDidMount: function() {
this.refs.inner.resize();
},
render: function() {
return (
<div className="component-section">
<Panel ref="inner"/>
</div>
);
}
});
var MainComponent = React.createClass({
render: function() {
return (
<ComponentSection />
);
}
});
Here is a working JSFiddlle.
In React JSX it does not appear to be possible to do something like this:
render: function() {
return (
<{this.props.component.slug} className='text'>
{this.props.component.value}
</{this.props.component.slug}>
);
}
I get a parse error: Unexpected token {. Is this not something React
can handle?
I'm designing this component so that under the hood, the values stored in this.props.component.slug will contain valid HTML elements (h1, p, etc.). Is there any way to make this work?
You should not put component slug in curly braces:
var Hello = React.createClass({
render: function() {
return <this.props.component.slug className='text'>
{this.props.component.value}
</this.props.component.slug>;
}
});
React.renderComponent(<Hello component={{slug:React.DOM.div, value:'This is my header'}} />, document.body);
Here is a working fiddle: http://jsfiddle.net/kb3gN/6668/
Also, you can find JSX Compiler helpful for debugging these kind of errors:
http://facebook.github.io/react/jsx-compiler.html
As nilgun previously pointed out, the component slug should not be wrapped in curly braces.
If you decide to store it in a variable, make sure it starts with a capital letter.
Here is an example:
var Home = React.createClass({
render: function() {
return (
<div>
<h3>This is an input</h3>
<CustomComponent inputType="input" />
<h3>This is a text area</h3>
<CustomComponent inputType="textarea" />
</div>
);
}
});
var CustomComponent = React.createClass({
render: function() {
// make sure this var starts with a capital letter
var InputType = this.props.inputType;
return <InputType />;
}
});
React.render(<Home />, document.getElementById('container'));
Here's a working fiddle: https://jsfiddle.net/janklimo/yc3qcd0u/
If your intention is to inject the actual component rendered, you can do something like this, which is very convenient for testing, or whatever reason you would want to dynamically inject components to render.
var MyComponentF=function(ChildComponent){
var MyComponent = React.createClass({
getInitialState: function () {
return {
};
},
componentDidMount: function () {
},
render: function () {
return (
<div className="MyComponent">
<ChildComponent></ChildComponent>
</div>
);
}
});
return MyComponent;
};
var OtherComponentF=function(){
var OtherComponent = React.createClass({
getInitialState: function () {
return {
};
},
componentDidMount: function () {
},
render: function () {
return (
<div className="OtherComponent">
OtherComponent
</div>
);
}
});
return OtherComponent;
};
var AnotherComponentF=function(){
var AnotherComponent = React.createClass({
getInitialState: function () {
return {
};
},
componentDidMount: function () {
},
render: function () {
return (
<div className="AnotherComponent">
AnotherComponent
</div>
);
}
});
return AnotherComponent;
};
$(document).ready(function () {
var appComponent = MyComponentF(OtherComponentF());
// OR
var appComponent = MyComponentF(AnotherComponentF());
// Results will differ depending on injected component.
ReactDOM.render(React.createElement(appComponent), document.getElementById("app-container"));
});
Edit: Maybe you forgot to add /** #jsx React.DOM */ at the beginning of js?
You can use React.DOM though:
render: function() {
return React.DOM[this.props.component.slug](null, this.props.component.value);
}
http://jsbin.com/rerehutena/2/edit?html,js,output
I am not a React expert, but I think every component should be construct with a specific tag at the beginning. So it could present a clear purpose itself.
The solution for me was to assign the imported Component to a variable(with CapitalCase) and then render that variable.
Example:
import React, { Component } from 'react';
import FooComponent from './foo-component';
import BarComponent from './bar-component';
class MyComponent extends Component {
components = {
foo: FooComponent,
bar: BarComponent
};
//this is the most important step
const TagName = this.components.foo;
render() {
return <TagName />
}
}
export default MyComponent;