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.
Related
I want to use leader-line in my React web project. It is an external javascript library, but I don't know how to integrate it into the project with the JSX syntax.
For example, its documentation tells us the general implementation:
Html
<div id="start">start</div>
<div id="end">end</div>
Javascript
// Add new leader line from `start` to `end` (HTML/SVG elements, basically).
new LeaderLine(
document.getElementById('start'),
document.getElementById('end')
);
How should I write in JSX file?
I try to write below, but failed.
import React, { Component } from 'react';
import LeaderLine from 'leader-line'
class Page extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
new LeaderLine(document.getElementById('start'),
document.getElementById('end'));
}
render() {
return (
<div className="Page">
<div id="start"></div>
<div id="end"></div>
</div>
)
}
}
export default Page;
This is the npm package page of leader-line.
Depending on what you are trying to achieve with leader-line, you may find that you can achieve it just as well with react-xarrows.
https://www.npmjs.com/package/react-xarrows
React-xarrows can be integrated into a React app much more easily (even using DOM identifiers rather than React Refs, if you prefer).
See this example code (taken directly from the link above), showing usage.
import React, { useRef } from "react";
import Xarrow from "react-xarrows";
const boxStyle = {
border: "grey solid 2px",
borderRadius: "10px",
padding: "5px",
};
function SimpleExample() {
const box1Ref = useRef(null);
return (
<div
style={{ display: "flex", justifyContent: "space-evenly", width: "100%" }}
>
<div ref={box1Ref} style={boxStyle}>
hey
</div>
<p id="elem2" style={boxStyle}>
hey2
</p>
<Xarrow
start={box1Ref} //can be react ref
end="elem2" //or an id
/>
</div>
);
}
I've made a small prototype to illustrate how it could be achieved.
class Line extends React.Component {
componentDidMount () {
this.waitWhenRefIsReady();
// scroll and resize listeners could be assigned here
}
componentWillUnmount () {
if(this.timer) {
clearInterval(this.timer);
}
}
shouldComponentUpdate () {
setTimeout(() => {
// skip current even loop and wait
// the end of parent's render call
if(this.line) {
this.line.position();
}
}, 0);
// you should disable react render at all
return false;
}
waitWhenRefIsReady () {
// refs are generated via mutations - wait for them
this.timer = setInterval(() => {
if(this.props.start.current) {
clearInterval(this.timer);
this.drawLine();
}
}, 5);
}
drawLine () {
const {start, end} = this.props;
this.line = new LeaderLine(start.current, end.current);
}
render () {
return null;
}
}
class App extends React.Component {
constructor (props) {
super(props);
this.state = {
left: 0,
};
this.myRef1 = React.createRef();
this.myRef2 = React.createRef();
}
componentDidMount() {
this.animateLine();
}
animateLine() {
setInterval(() => {
const limit = 200;
const {left} = this.state;
const x = ((left % limit) + limit) % limit;
this.setState({left: x + 10});
}, 1000);
}
render () {
const {left} = this.state;
const {myRef1, myRef2} = this;
return <div className="container">
<Line
start={this.myRef1}
end={this.myRef2} />
<div
id="start"
ref={this.myRef1}
style={{
left: `${left}px`
}}></div>
<div
id="end"
ref={this.myRef2}></div>
</div>
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
Leader Line + React JSX simple prototype
import LeaderLine from 'leader-line';
Add in leader-line.min.js (at end)
if (module && module.exports) { module.exports = LeaderLine }
Refer to this thread for how to integrate Leaderline into your react project :
https://github.com/anseki/leader-line/issues/8#issuecomment-370147614
in summary,
you cant just do
import LeaderLine from 'leader-line';
to import LeaderLine, because its not an ES2015 module yet!
similar to how #shilpa pointed out,
you can tweak the webpack config to include -
rules: [
{
test: require('path').resolve(__dirname, 'node_modules/leader-line/'),
use: [{
loader: 'skeleton-loader',
options: {procedure: content => `${content}export default LeaderLine`}
}]
}
and then inside componentDidMount, you could do
new LeaderLine(document.getElementById('start'),
document.getElementById('end'));
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 trying to hide an image by default and only show it when element is hovered. I've been able to set the default state etc.. Only issue is creating an if statement that will show and hide the image.
This is the component:
import React from 'react';
import { Link } from 'react-router';
import Eyecon from '../../static/eye.svg';
class Item extends React.Component {
constructor(props) {
super(props);
this.displayName = 'Item';
this.handleHover = this.handleHover.bind(this);
this.state = {
hover: false
};
}
mouseOver() {
this.state.hover = true;
}
mouseOut() {
this.state.hover = false;
}
handleHover() {
console.log("hover");
}
render() {
const { item, i } = this.props;
return (
<div className="grid-box">
<img src={Eyecon}/>
</div>
)
}
}
export default Item;
I've tried a few things, but also want to see what the best practice is.
Thanks for your time
There are multiple ways, I would do it like this:
render() {
const { item, i } = this.props;
return (
<div className="grid-box">
{this.state.hover ? (
<img src={Eyecon} />
) : null}
</div>
)
}
But you could also abstract the image rendering into a separate function and not return anything when needed.
Sitenote: You shouldn't mutate the state directly. Use the this.setState() function. Otherwise the component will not be re-rendered.
Also, may I ask why you're not just using css :hover to achieve this behaviour?
I typically like to handle conditional displaying of content in helper functions, like so:
function renderImage() {
const { hover } = this.state;
if (hover) {
return (
<img src={Eyecon} />
);
}
}
Then, you can just call this function from render()
render() {
const { item, i } = this.props;
return (
<div className="grid-box">
{renderImage.call(this)}
</div>
)
}
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>);
}
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;