Slider Carousel JS, React - javascript

Who can help with that? I need to write everything in a normal algorithm, not as it is now. I have three elements. I wanted to make it so that when I'm on the last one, the next button takes me back to the first element. To make a infinity loop carrousel. project at the react
import React, { useEffect, useState } from "react";
import { factory_img, factory_bg_svg } from "#/img_video";
export default function Factory_Video() {
const arr_items = []
useEffect(() => {
const items = document.querySelectorAll('.item')
for (let item of items) {
arr_items.push(item)
}
})
function next_slide() {
const el_1 = arr_items[0].classList.contains("active")
const el_2 = arr_items[1].classList.contains("active")
if (el_1) {
arr_items[0].classList.remove('active')
arr_items[0].classList.add("transform")
arr_items[1].classList.add("active")
arr_items[1].classList.remove('transform')
} else if (el_2) {
arr_items[1].classList.remove('active')
arr_items[1].classList.add("transform")
arr_items[2].classList.add("active")
arr_items[2].classList.remove('transform')
}
}
}
return (
<section className="section_factory" >
<img id="bg_section_factory" src={factory_bg_svg} alt="" />
<div className="container_factory">
<h1 className="h1_section_title" >О производстве <br /> Венарус</h1>
<div className="wrapper">
<div className="window">
<div className="item active" >
<img src={factory_img} />
</div>
<div className="item transform" >
<img src={factory_img} />
</div>
<div className="item transform" >
<img src={factory_img} />
</div>
</div>
<div className="navigation" >
<div className="btn_prev" ></div>
<div onClick={next_slide} className="btn_next" ></div>
</div>
</div>
</div>
</section>
);
}
I tried using array methods, but it didn't work.

The code is not complete so I can't tell the exact changes you need to do, however here are a list of recommendations I can see your code lacks or are wrong.
Never manipulate the dom directly with React.
Modifying the classlist in a function is not the React way and is prone to errors as it will be cleared if a re-render is triggered. Instead change the classes directly in the html.
It would be something like <div className={"item " +active===0?'active':''} >
You're declaring const arr_items = [] directly above useEffect. This varible will be cleared on every render. There are other ways to keep the info around in React, however in this case there is no point to keep the array of dom elements.
Your useEffect has no dependencies (the array sent as second parameter). This makes it execute every time the component runs so there's no point in using a useEffect here. (btw you don't need it at all for this to work)
Finally the "React" way (at least one approach) would be to have a state with the index of the active element. You just have to change this index with its corresponding setter and render the classes of each element conditionally.

Related

Add div onclick using React, Gatsby, JavaScript

Hello I am trying to add another container div on click. With my current code I am getting the error TypeError: document.getElementById(...) is null. I got this answer from a stackoverflow problem and I beleive it does not work because I am using react/gatsby.
I have also tried having the <button id="click"></button> inside of trello.js and I am still getting the same error.
Trello.js:
import React from 'react'
import "./style.css"
import Button from "./Button"
export default function Trello() {
const draggables = document.querySelectorAll('.draggable')
const containers = document.querySelectorAll('.container')
draggables.forEach(draggable => {
draggable.addEventListener('dragstart', () => {
draggable.classList.add('dragging')
})
draggable.addEventListener('dragend', () => {
draggable.classList.remove('dragging')
})
})
containers.forEach(container => {
container.addEventListener('dragover', () => {
const draggable = document.querySelector('.dragging')
container.appendChild(draggable)
})
})
return (
<div>
<Button />
<div id="main-div">
<div>
<div class="draggable" draggable="true">Jason</div>
<div class="draggable" draggable="true">Jack</div>
<div class="draggable" draggable="true">Caleb</div>
<div class="draggable" draggable="true">Ransom</div>
</div>
<body>
<div class="container">
<p></p>
</div>
<div class="container">
<p></p>
</div>
<div class="container">
<p></p>
</div>
</body>
</div>
</div>
)
}
Button.js
import React from 'react'
export default function Button() {
document.getElementById('click').onclick = function () {
var div = document.createElement('div')
div.className = 'container'
document.getElementsByTagName('body')[0].appendChild(div)
}
return (
<div>
<button id="click"></button>
</div>
)
}
You are trying to access document (to get the identifier) on processing render time where it isn't defined yet, as Gatsby documentation explains:
Some of your code references “browser globals” like window or
document. If this is your problem you should see an error above like
“window is not defined”.
That's the reason why it is null in your snippet. If you want to do this, you may need to use a useEffect/componentDidMount hook/lifecycle. Both methods await and trigger once the component output has been rendered to the DOM.
However, accessing directly to the DOM in React is strongly not recommended, indeed, that's why you are using React, to create and manipulate a virtual DOM, where the cost (in terms of efficiency) for the browser to render and rerender it, instead of changing itself is the great value of React. Of course, it is not prohibited but you should avoid it. Instead, you can use useRef hook.
The last thing is that you may want to use something like this to achieve your goal is something like this:
import React from 'react'
export default function Button() {
const createDiv =()=> <div className={`container`}>'Im a new div</div>
return (
<div>
<button onClick={createDiv}>Click me</button>
</div>
)
}
The snippet above will create a new <div>, with "I'm a new div" inside as a return of your onClick function. If you need to create a new <div>on every click, the snippet needs a few changes, let me know and I'll update it.
import React from "react";
export default function Button() {
let click = () => {
var div = document.createElement("div");
div.innerHTML = "New DIV";
div.className = "container";
document.getElementsByTagName("body")[0].appendChild(div);
};
return (
<div>
<button onClick={click}>Click Me</button>
</div>
);
}

Unexpected behaviour : function vs functional component and animations

I display a list of foos and when i click on some link more results i keep the existing foos and i append to them the new ones from my api like bellow
const [foos, setFoos] = useState([]);
...
// api call with axios
...
success: (data) => {
setFoos([ ...foos, ...data ])
},
Each <Foo /> component run the animation above
App.js
...
<div className="foos-results">
{ foos.map((foo, index) => <Foo {...{ foo, index }} key={foo.id}/>) }
</div>
...
Foo.js
const Foo = ({ foo, index }) => <div className="circle">...</div>
animation.css
.circle {
...
animation: progress .5s ease-out forwards;
}
The problem is when i append the new ones then the animation is triggered for all the lines of <Foo />.
The behavior expected is that the animation is triggered just for the new ones and not starting over with the existing ones too.
UPDATE
We have found the origin of the problem (it's not related to the uniqueness of key={foo.id})
if we change
const Foo = ({ foo, index }) => <div className="circle">...</div>
to
const renderFoo = ({ foo, index }) => <div className="circle">...</div>
And App.js to
...
<div className="foos-results">
{ foos.map((foo, index) => renderFoo({ foo, index })) }
</div>
...
It works
So why is this behavior like this in react ?
here is a sandbox based on #Jackyef code
This is quite an interesting one.
Let's look at the sandbox provided in the question.
Inside App, we can see this.
const renderItems = () => (
<div>
{items.map((item, index) => (
<div className="item" key={item.id}>
<span>
{index + 1}. {item.value}
</span>
</div>
))}
</div>
);
const Items = () => renderItems();
return (
<div className="App">
<h1>List of items</h1>
<button onClick={addItem}>Add new item</button>
<Items />
</div>
);
Seems pretty harmless right? The problem with this is that Items is declared in the App render function. This means that on each render, Items actually is now a different function, even though what it does is the same.
<Items /> is transpiled into React.createElement, and when diffing, React takes into account each components' referential equality to decide whether or not it is the same component as previous render. If it's not the same, React will think it's a different component, and if it's different, it will just create and mount a new component. This is why you are seeing the animation being played again.
If you declare Items component outside of App like this:
const Items = ({ items }) => (
<div>
{items.map((item, index) => (
<div className="item" key={item.id}>
<span>
{index + 1}. {item.value}
</span>
</div>
))}
</div>
);
function App() { /* App render function */}
You will see everything works as expected. Sandbox here
So, to summarise:
Referential equality matters to React when diffing
Components (function or class that returns JSX) should be stable. If they change between renders, React will have a hard time due to point number 1.
I don't think there is a way to disable this re-rendering animation, but I think there is a workaround that could solve this issue.
As we know that each div's css is reloaded every time, so the solution I can think of, is to create another css class rule (let this class be named 'circle_without_anim') with same css as class 'circle' but without that animation and while appending new div, just before appending change class of all divs that have class name 'circle' to 'circle_without_anim' that would make the changes and css to previous divs but just without that animation and the append this new div with class 'circle' making it the only div that have animation.
Formally the algorithm will be like:
Write another css class(different name for example prev_circle) with same rules as 'circle' but without the animation rule.
In Javascript just before appending new div with class 'circle', change class of all previous divs that have class named 'circle' to newly created class 'prev_circle' that do not have animation rule.
Append the new div with class 'circle'.
Result: It would give an illusion that the CSS of previous divs is not being reloaded as the css is same but without animation, but the new div has different css rule (animation rule) which is going to be reloaded.
With this code:
const Items = () => renderItems();
...
<Items />
React has no chance of knowing that Items in the current render is the same component as Items in the previous render.
Consider this:
A = () => renderItems()
B = () => renderItems()
A and B are different components, so if you have <B /> in the current render and <A /> instead of <B /> in the previous render, React will discard the subtree rendered by <A /> and render it again.
You are invoking React.createElement (since <Items /> is just a JSX syntax sugar for React.createElement(Items, ...)) every render, so React scraps the old <Items /> in the DOM tree and creates it again each time.
Check out this question for more details.
There are two solutions:
create Items component outside of the render function (as Jackyef suggested)
use render function ({ renderItems() } instead of <Items />)

React styling/component not showing (MS Fabric UI)

I have a list that I want to style with Microsoft fabrics react "list" component as seen here:
https://developer.microsoft.com/en-us/fabric#/components/list
(the one with the "Rendering ghost items while the list is scrolling" header).
I try to follow the examples and do the folowing:
import { List } from 'office-ui-fabric-react/lib/List';
import { Image, ImageFit } from 'office-ui-fabric-react/lib/Image';
return (
<div>
{ listItems.length > 0 && listItems.map((item: Project) => {
return (
<div>
<div data-is-focusable={true} className="ms-ListGhostingExample-
itemCell">
<Image
className="ms-ListGhostingExample-itemImage"
src={item.ListUrl.substring(0, item.ListUrl.indexOf("/Lists")) + "/_api/GroupService/GetGroupImage?id" }
width={50}
height={50}
imageFit={ImageFit.cover}
/>
<div className="ms-ListGhostingExample-itemContent">
<div className="ms-ListGhostingExample-itemName"> <a href={item.ListUrl.substring(0, item.ListUrl.indexOf("/Lists"))}>{item.Title}</a></div>
</div></div>
</div>
);
})}
</div>
While I don't get any (syntax) errors, no styling is applied at all. Why does it not work?
In Rendering ghost items while the list is scrolling example via the following reference:
imports import './List.Ghosting.Example.scss';
the expected styling is applied per a List component. In your case those styles could be applied like this:
import 'office-ui-fabric-react/lib/components/List/examples/List.Ghosting.Example.scss';

Toggle prop passed on one of many targets in ReactJS

Just starting off with ReactJS and have a project where I am showing an accordion of issues and including a details area that is hidden on the start.
There is a button in the accordion bar that should pass a prop to the child element to hide or show them. I have refs on the button and on the details child compoment and added a function to call the function and pass the ref of the details area. I am just not sure how to dynamically change the class hidden on one of many areas and not all of them.
Not sure if putting a class on each element and then learning how to toggle the particular child's class is better or changing the prop to the child.
I can get to the change function but am drawing a blank from there and all the googling shows how to do one element with a grand change of state but I need individual elements.
Here is what I have so far.
Parent
...
<AccordionItem key={item.id} className={iconClass} title={`${item.area}`} expanded={item === 1}>
{
item.issues.map(issue => {
let trim = (issue.issue.length>21) ? `${issue.issue.substring(0,22)}...`: issue.issue;
return (
<div className="issue-bar container-fluid">
<div className="row issue-bar-row">
<span className="issue-title"><img src={CriticalRed} alt="Critical"/> {trim}</span>
<span className="btns">
<button className="btn btn-details" onClick={() => this.showDetail(`details-${issue.id}`)}>Details</button>
</span>
</div>
<IssuesDetails ref={`details-${issue.id}`} issue={issue} shouldHide={true} />
</div>
)
})
}
<div>
</div>
</AccordionItem>
...
Child
export default class IssuesDetails extends Component{
render(){
let issueDetails = classNames( 'issue-details', { hidden: this.props.shouldHide } )
return(
<div className={issueDetails}>
<div className="issues-details-title">
<h3>{this.props.issue.issue}</h3>
</div>
<div className="issues-details-details">
{this.props.issue.details}
</div>
<div className="issues-details-gallery">
<ImageGallery source={this.props.issue.photos} showPlayButton={false} useBrowserFullscreen={false} />
</div>
<button className="btn btn-success">Resolve</button>
</div>
)
}
}
Thanks for any help you provide or places you can send me!
If i'm understanding correctly, you need to be able to swap out shouldHide={true} in certain circumstances. To do this, you'll want your parent component to have a state object which indicates whether they should be hidden or not.
Exactly what this state object looks like depends on what sort of data you're working with. If the issues is a single array, then perhaps the state could be an array of booleans indicating whether each issue is expanded or not. I suspect you may have a more nested data structure, but i can't tell exactly since some of the code was omitted.
So assuming you have an array, it might look like this (i've omitted some things from the render method for brevity):
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
hidden: (new Array(props.issues.length)).fill(false),
};
}
showDetail(index) {
let newHidden = this.state.hidden.slice();
newHidden[index] = true;
this.setState({
hidden: newHidden
});
}
render() {
return (
<AccordionItem>
{this.props.issues.map((issue, index) => {
<div>
<button onClick={() => this.showDetail(index))}/>
<IssuesDetails issue={issue} shouldHide={this.state.hidden[index]}/>
</div>
})}
</AccordionItem>
);
}
}
Take a look at these:
https://codepen.io/JanickFischr/pen/xWEZOG
style={{display: this.props.display}}
I think it will help with your problem. If you need more information, please just ask.

Passing data up in an onClick function in React

I'm new to React/ES6 and I'm working on my first components. I have a PuzzleContainer component that contains a Puzzle component which displays images. The container component makes an AJAX call to determine what images to display and then passes that data down to the child component. I want to do something in the container component when one of the images is clicked.
Here is my code, I didn't include the componentWillMount() function in the container component, but cards is just an array of strings.
class PuzzleContainer extends React.Component {
cardClicked(cardsrc) {
console.log(cardsrc);
}
render() {
return (
<div>
<section className="wrapper site-min-height" id="main-wrapper">
<Puzzle cards={this.state.cards} cardClicked={this.cardClicked}></Puzzle>
</section>
<Infobar></Infobar>
</div>
);
}
}
class Puzzle extends React.Component {
render() {
var cards = this.props.cards;
var html = cards.map((card) =>
<div className="col-lg-2 col-md-4 col-sm-6 col-xs-12 desc">
<div className="photo-wrapper">
<div className="photo">
<img className="img-responsive" src={card} onClick={(card) => this.props.cardClicked(card)}></img>
</div>
</div>
</div>
);
return (
<div className="row mt gutter">{html}</div>
);
}
}
The problem is, whenever I click an image, a Proxy(?) Object gets printed to the console. It seems to be some object from React. However, my understanding was that it should just print the card value that gets passed to the function, which is a string.
Why is an object getting logged instead of a string? How do I make it so the container component can receive the identity of the image that gets clicked?
You use card as both the parameter for the map function as well as the parameter for the onClick function. As a result, the onClick event object is being passed to this.props.cardClicked rather than the string you desire.
Changing your img tag to
<img className="img-responsive" src={card} onClick={() => this.props.cardClicked(card)}></img>
will do what you want.
The first parameter of an onClick handler is the event object.
onClick={(card) => this.props.cardClicked(card)} When you do this, the event object is assigned the name card and you are actually passing the event object to this.props.cardClicked instead of the card variable that you are looping through in map.
Do this instead:
<img className="img-responsive"
src={card}
onClick={this.props.cardClicked.bind(this, card)}
/>
Btw, <img> is self-closing and you can combine the opening and closing tag into one.

Categories

Resources