How to appendTo React Components as simple as jQuery can - javascript

As I try to get into React I find I'm running through lists of things I can do very simply in jQuery but are an absolute nightmare in React.
https://codepen.io/PocketNinjaDesign/pen/boJoEd
have modified the goal from 2 elements to nth
The goal is that nth elements on the page can popup inside of any element / component I want on the page. I achieve this the good old fashion way by adding a data attribute containing Json with an array of components to appendTo.
You can also use JS to turn a component into a ninja and passing an object through containing the list of components to appear in.
$(function() {
$('.other-ninja').Ninja({
components: ['title', 'header', 'angry']
});
});
So, imagining these are React Components now, all different kinds of components, but the ninja block(s) can be told they can appear in any components they want.
How is that possible with React without going through a ball ache of declaration and indigestion?
Here is my code for the jQuery popup appendTo script as what I want to achieve in React.
Have altered the code since the answer I received as I don't think I
was putting my question across correctly.
HTML
<div class="ninja" data-ninja='{"components": ["title", "happy", "sad", "stinky", "header", "AnotherComponent"]}'></div>
<div class="mongoose" data-ninja='{"components": ["happy", "sad", "stinky", "angry", "footer"]}'></div>
<div class="other-ninja"></div>
<h1 class="title">Getting React to work like simple jQuery :-D</h1>
<p>Where ALL HTML elements on this page represent React Components. All components being
split into different files and imported using babel es6 compiler</p>
<div class="header">
<div class="angry"></div>
</div>
<div class="content">
<ul>
<li>Just a list showing more component depth</li>
<li>
<div class="someOtherComponent">
<div class="sad"></div>
</div>
</li>
<li>Trying to show the code works regardless of where the elements are</li>
</ul>
<div class="AnotherComponent">
<div class="SomeOtherComponent">
<div class="WhatAnotherComponent"><div class="happy"></div></div>
</div>
</div>
</div>
<div class="footer">
<div class="whatever">
<div class="stinky"></div>
</div>
</div>
SCSS
Removed the SCSS as it was detracting from the main focus of markup and javascript
Script
$(function() {
function Ninja(e, options) {
var $this = $(e);
options = $.extend({}, $this.data('ninja'), options);
var componentList = options.components;
setInterval(function() {
var randomNum = Math.floor(Math.random() * componentList.length);
$this.appendTo('.' + componentList[randomNum]);
}, 1000);
}
$.fn.Ninja = function(options) {
$(this).each(function(i, e) {
Ninja(e, options);
});
};
$('[data-ninja]').each(function(i, e) {
$(e).Ninja();
});
});
$(function() {
$('.other-ninja').Ninja({
components: ['title', 'header', 'angry']
});
});

React might seem like "nightmare" when you used to use jQuery but actually using React is really easy. You just need to adjust your coding logic to suit React guidelines.
The code you shared can be implemented in React many different ways. I did a small working version for you to compare. At first-look it might seem a lot more code to achieve same simple affect but React has a lot more control over the DOM (Virtual DOM) and can be manipulated a lot more different ways. Most of the code in this example is same for all components. You just need to put consideration render method. Appending or removing a component/element can be achieved by just a simple if statement. Also component logic gives you a big flexibility and re-usability. There are a lot of good examples and tutorials online that can show you how you can solve thing in react way. A really good start point is React Docs and awesome-react.
import React from 'react';
import { render } from 'react-dom';
const styles = {
component: {
position: 'absolute',
width: 100,
height: 100
},
happy: {
position: 'absolute',
width: 100,
height: 100,
top: 100,
left: 40,
backgroundColor: 'yellow'
},
sad: {
position: 'absolute',
width: 100,
height: 100,
top: 20,
left: 180,
backgroundColor: 'blue'
},
angry: {
position: 'absolute',
width: 100,
height: 100,
top: 160,
left: 500,
backgroundColor: 'red',
},
stinky: {
position: 'absolute',
width: 100,
height: 100,
top: 210,
left: 300,
backgroundColor: 'green'
},
ninja: {
position: 'absolute',
zIndex: 100,
width: 50,
height: 50,
backgroundColor: '#000'
},
deathstar: {
position: 'absolute',
zIndex: 100,
width: 20,
height: 20,
backgroundColor: '#444'
}
};
const Ninja = () => (<div style={styles.ninja}></div>);
const DeathStar = () => (<div style={styles.deathstar}></div>);
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
types: ['happy', 'sad', 'angry', 'stinky'],
ninja: 0,
deathstar: 0
};
this.intervalNinja = null;
this.intervalDeathstart = null;
}
randomNumber() {
return Math.floor(Math.random() * (3 - 0 + 1)) + 0;
}
componentWillUnmount() {
clearInterval(this.intervalNinja);
clearInterval(this.intervalDeathstart);
this.intervalNinja = null;
this.intervalDeathstart = null;
}
componentDidMount() {
this.intervalNinja = setInterval(() => {
this.setState({
ninja: this.randomNumber()
});
}, 1082);
this.intervalDeathstart = setInterval(() => {
this.setState({
deathstar: this.randomNumber()
});
}, 987);
}
render() {
return(
<div>
{this.state.types.map((type, index) => (
<div style={styles[type]}>
{this.state.ninja === index && <Ninja />}
{this.state.deathstar === index && <DeathStar />}
</div>
))}
</div>
)
}
}
render(<App />, document.getElementById('root'));

Related

Is this the right way to import gsap in vue.js (it works but is it the "right" way?)

I'm quite new to Vue.js and have had some problems getting libraries to work without getting the "error 'X' is not defined no-undef" message.
In this case it is 'Back' that is not defined (which is a part of GSAP)
I figured the only place to "define" Back would be in the import.
Is this just the way to import libraries?
Do I have to write every undefined part in the import like this?
It works but it just seems unnecessary.
<template>
<div id="mainTemplate">
<h2>This is the MainTemplaye.vue Component</h2>
<div ref="box" class="box"></div>
</div>
</template>
<script>
import { TimelineLite, Back } from "gsap";
export default {
name: "MainTemplate",
mounted() {
const { box } = this.$refs;
const timeline = new TimelineLite();
timeline.to(box, 1, { x: 200, rotation: 90, ease: Back.easeInOut, })
timeline.to(box, 0.5, { background: 'green' },'-=0.5')
},
};
</script>
<style>
.box {
height: 60px;
width: 60px;
background: red;
}
</style>
I'm not sure where you're learning from, but you're using the old syntax of GSAP. If you use the new syntax of GSAP you don't have to import anything other than gsap in your case:
import { gsap } from "gsap";
export default {
name: "MainTemplate",
mounted() {
const { box } = this.$refs;
const timeline = gsap.timeline();
timeline.to(box, { duration: 1, x: 200, rotation: 90, ease: 'back.inOut' })
timeline.to(box, { background: 'green' }, '-=0.5')
},
};
The best place to start learning is the official GSAP Getting Started article.

How to make chat like UI with chat bubbles in React JS

I have some JSON data in dummyData. I am not sure how can I place the chat bubbles on left and right according to the direction. I am using Material UI and context API. Image for the reference. I don't want to use any library other than material UI.
Currently, every chat bubble is positioned to the left. How to position bubbles according to the direction. Code so far (CodeSandbox):
import React from 'react';
import makeStyles from '#material-ui/core/styles/makeStyles';
const useStyles = makeStyles(theme => ({
container: {
bottom: 0,
position: 'fixed'
},
bubbleContainer: {
width: '100%'
},
bubble: {
border: '0.5px solid black',
borderRadius: '10px',
margin: '5px',
padding: '10px',
display: 'inline-block'
}
}));
const ChatLayout = () => {
const classes = useStyles();
const dummyData = [
{
message: '1: This should be in left',
direction: 'left'
},
{
message: '2: This should be in right',
direction: 'right'
},
{
message: '3: This should be in left again',
direction: 'left'
}
];
const chatBubbles = dummyData.map((obj, i = 0) => (
<div className={classes.bubbleContainer}>
<div key={i++} className={classes.bubble}>
<div className={classes.button}>{obj.message}</div>
</div>
</div>
));
return <div className={classes.container}>{chatBubbles}</div>;
};
export default ChatLayout;
You can create separate div of chat bubble and apply CSS. And where you are receiving messages append the bubble div to your user list.

Dynamic components: Calling element by ref

One part of my application is an image gallery. When the user clicks on an image, I want to put an opaque layer over the image to visualize that it is selected.
When I display the layer, and I click on the image to deselect it, naturally I'm actually clicking on the layer.
Here's the relevant ReactJS code to show what I mean:
{images.map((i, idx) => (
<div key={"cont"+idx} className="container">
<img src={i.images} ref={"img"+idx} />
<div onClick={this.handleIconDeselect} id={"div_"+idx}></div>
</div>
)
)}
I tried to give the img a unique ref (as shown above), but I'm having trouble selecting the correct img.
This is how I try to select the correct image:
handleIconDeselect = (event) => {
var imgref = "icon"+event.target.id.split("_").pop();
this.refs.imgref.click();
}
However, I get the following error message:
TypeError: Cannot read property 'click' of undefined
How can I select the correct image while using unique refs?
Alternatively, if the way I'm trying to achieve this is bad practice (I know you should only use refs when absolutely necessary), what is a better way to do it?
Try use state as here: https://codesandbox.io/s/m4276x643y
Maybe that is not the best way but it give you an rough idea.
import React, { Component } from "react";
import { render } from "react-dom";
import Hello from "./Hello";
const coverStyle = {
position: "fixed",
top: 0,
left: 0,
zIndex: -1,
opacity: 0,
width: "100%",
height: "100%",
background: "#000"
};
const coverStyleShow = {
...coverStyle,
zIndex: 1,
opacity: 1
};
const imgShow = {
zIndex: 10,
position: "relative"
};
const images = [
"https://dummyimage.com/100.png/f10/fff",
"https://dummyimage.com/100.png/f20/fff",
"https://dummyimage.com/100.png/f30/fff",
"https://dummyimage.com/100.png/f40/fff",
"https://dummyimage.com/100.png/f50/fff",
"https://dummyimage.com/100.png/f60/fff",
"https://dummyimage.com/100.png/f70/fff"
];
class App extends Component {
constructor(props) {
super(props);
this.state = {
cover: coverStyle,
img: imgShow,
imgId: null,
imgShow: false
};
}
handleImageClick = (target, idx) => {
// you can do something with this "target"...
this.setState({
cover: coverStyle,
coverShow: coverStyleShow,
imgId: idx,
imgShow: !this.state.imgShow
});
};
render() {
return (
<div>
<Hello name="CodeSandbox" />
<h2>Start editing to see some magic happen {"\u2728"}</h2>
<div>
{images.map((img, idx) => (
<img
key={img}
src={img}
style={idx === this.state.imgId ? this.state.img : null}
onClick={event => this.handleImageClick(event.target, idx)}
alt="dummy img"
/>
))}
</div>
<span
style={this.state.imgShow ? this.state.coverShow : this.state.cover}
/>
</div>
);
}
}
render(<App />, document.getElementById("root"));

Using .map() to generate elements with Refs

Is there a best way to go about using .map() to output elements with refs?
I'm doing this so I can track the elements (generated from state) for collision detection in an endless runner style game (without canvas).
I’ve tried 3 different approaches and matter what I do I seem to get ref=ref() when it should be ref=“tango” (as per the state). Image here - https://i.imgur.com/oeStcHb.png
EDIT: for others in future, I have since learned that ref=ref() is expected behaviour to indicate a callback is associated with it, instead of the old way of showing the actual ref.
Code being used-
this.state = {
people: {
tango: { height: 10, weight: 200 },
cash: { height: 20, weight: 300 }
}
};
...
outputStatePeople(key) {
return(<span className="db" key={key} ref={key}>name: {key}</span>)
}
...
render() { return (
<div>
{Object.keys(this.state.people).map(key => this.outputStatePeople(key))}
</div>
)}
The above use a function to generate dom, but this also happens if outputting inline html and when using a component.
Any help would be greatly appreciated! 😀
Define a function for a ref and store the refs in the class variable object like
constructor(props) {
super(props);
this.state = {
people: {
tango: { height: 10, weight: 200 },
cash: { height: 20, weight: 300 }
}
};
this.domRefs = {};
}
outputStatePeople(key) {
return (
<span className="db" key={key} ref={(ref) => {this.domRefs[key] = ref}}>
name: {key}, height: {this.state.people[key].height}
</span>
);
}
You can read more about Refs and Dom Here

How to use React TransitionMotion willEnter()

Using React Motion's TransitionMotion, I want to animate 1 or more boxes in and out. When a box enters the view, it's width and height should go from 0 pixels to 200 pixels and it's opacity should go from 0 to 1. When the box leaves the view, the reverse should happen (width/height = 0, opacity = 0)
I have tried to solve this problem here http://codepen.io/danijel/pen/RaboxO but my code is unable to transition the box in correctly. The box's style jumps immediately to a width/height of 200 pixels instead of transitioning in.
What is wrong with the code?
let Motion = ReactMotion.Motion
let TransitionMotion = ReactMotion.TransitionMotion
let spring = ReactMotion.spring
let presets = ReactMotion.presets
const Demo = React.createClass({
getInitialState() {
return {
items: []
}
},
componentDidMount() {
let ctr = 0
setInterval(() => {
ctr++
console.log(ctr)
if (ctr % 2 == 0) {
this.setState({
items: [{key: 'b', width: 200, height: 200, opacity: 1}], // fade box in
});
} else {
this.setState({
items: [], // fade box out
});
}
}, 1000)
},
willLeave() {
// triggered when c's gone. Keeping c until its width/height reach 0.
return {width: spring(0), height: spring(0), opacity: spring(0)};
},
willEnter() {
return {width: 0, height: 0, opacity: 1};
},
render() {
return (
<TransitionMotion
willEnter={this.willEnter}
willLeave={this.willLeave}
defaultStyles={this.state.items.map(item => ({
key: item.key,
style: {
width: 0,
height: 0,
opacity: 0
},
}))}
styles={this.state.items.map(item => ({
key: item.key,
style: {
width: item.width,
height: item.height,
opacity: item.opacity
},
}))}
>
{interpolatedStyles =>
<div>
{interpolatedStyles.map(config => {
return <div key={config.key} style={{...config.style, backgroundColor: 'yellow'}}>
<div className="label">{config.style.width}</div>
</div>
})}
</div>
}
</TransitionMotion>
);
},
});
ReactDOM.render(<Demo />, document.getElementById('app'));
As per the documentation of styles under the TransitionMotion section (and I don't claim to have understood all of it entirely :)):
styles: ... an array of TransitionStyle ...
The key thing to note here is that there are 2 types of style objects that this library deals with (or at least this TransitionMotion part of it) and it calls them TransitionStyle and TransitionPlainStyle.
The previous values passed into styles attribute were of TransitionPlainStyle. Changing them to TransitionStyle magically starts animating the Enter sequence.
You can read more about 2 different types mentioned above over here.
styles={this.state.items.map(item => ({
key: item.key,
style: {
width: spring(item.width),
height: spring(item.height),
opacity: spring(item.opacity)
}
}))}
Forked codepen demo.
Again, I do not fully understand the inner workings of it just yet. I just know that your styles had to be changed in the above way to make it work.
I will be happy if someone can educate me on this as well.
Hope this helps.

Categories

Resources