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';
Related
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.
I am rendering a list of students, some of which have failed their exams. For those who have failed their exams, I display a red square behind their avatars.
Whenever I hover over a student's avatar I want to display the subject that student has failed. My issue at the moment is that I display the subjects for all students, not only the one I've hovered over.
How can I display only the mainSubject for the student who's avatar I hovered on?
Here is a link to my code sandbox: Example Link
I solved it like following.
Get the id of the hovered student. Match this id from the list of students you render. if its match then show the subjects
Also, I renamed the hook
add key prop
you can check this too https://codesandbox.io/s/zealous-bhaskara-mi83k
const [hoveredStudentId, setHoveredStudentId] = useState();
return (
<>
{students.map((student, i) => {
return (
<div className="student-card" key={i}>
<p>
{student.firstName} {student.lastName}
</p>
{student.passed === false ? (
<>
<img
id={student.id}
src={student.picture}
className="student-avatar fail"
onMouseEnter={e => {
setHoveredStudentId(e.currentTarget.id);
}}
onMouseLeave={e => {
console.log(e.currentTarget.id);
setHoveredStudentId(0);
}}
alt="avatar"
/>
{hoveredStudentId === student.id && (
<div className="subject-label">{student.mainSubject}</div>
)}
</>
) : (
<img
src={student.picture}
className="student-avatar"
alt="avatar"
/>
)}
</div>
);
})}
</>
);
Issue is that you have a list of students but only 1 flag to show/hide subjects.
Solution: 1
Maintain a list of flag/student. So you will have n flags for n students. Simple way for this is to have a state in a way:
IStudentDetails { ... }
IStudentStateMap {
id: string; // uniquely identify a syudent
isSubjectVisible: boolean;
}
And based on this flag isSubjectVisible toggle visibility.
Updated code
Solution 2:
Instead of handling it using React, use CSS tricks. Note this is a patch and can be avoided.
Idea:
Wrap Student in a container element and add a class onHover on elements on elements that needs to be shown on hover.
Then use CSS to show/hide those elements.
.student-container .onHover {
display:none;
}
.student-container:hover .onHover{
display: block;
}
This way there wont be rerenders and no need for flags.
Updated Code
However, solution 1 is better as you have more control and when you are using a UI library, its better to let it do all mutation and you should follow its ways.
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 />)
I am trying to use a semantic UI accordion containing multiple entries and allow more than one entry to be open at once; each entry has a title portion containing an icon with a popup attached and a content area containing a textarea.
I would like to be able to have both accordions open at the same time which is apparently supported by using the exclusive={false} prop when making the accordion element as described in the documentation here
But that example looks to be using an array of objects with content that is a string, not other react/html/jsx elements (in my case it is semantic ui icons, popups and textareas). That array of objects is passed in to the accordion's panel prop.
And I am unfamiliar with what the semantic ui react accordion requires to function correctly with keeping track of indices and other stuff, I am not sure what else I need to configure or if this is possible with the semantic ui component as is.
I essentially copied this example and use an active index and an onclick handler which switches the active index in the component react state.
Here is a snippet of the accordion and onclick handler and react app state:
class FileUpload extends Component {
// other stuff omitted
constructor(props) {
super(props);
this.state = {
activeAccordionIndex: -1
};
handleAccordionClick = (e, titleProps) => {
const { index } = titleProps;
const { activeAccordionIndex } = this.state;
const newIndex = activeAccordionIndex === index ? -1 : index;
this.setState({
activeAccordionIndex: newIndex
})
}
// I'm using a small helper function to create the accordion and invoke it in
// the render method, just one item for brevity; the other entries are pretty
// much the same
getAccordionInputs() {
const { activeAccordionIndex } = this.state;
let accordionContent = (
<Accordion fluid exclusive={false}>
<Accordion.Title
className="file-upload-ordinal-accord-title"
active={activeAccordionIndex === 0}
index={0}
onClick={this.handleAccordionClick}
>
<Icon name='dropdown' />
Enter Ordinal Features
<Popup
on="click"
position="right center"
header="Ordinal Features Help"
content={
<div className="content">
<p>Ordinal Features help description</p>
</div>
}
trigger={
<Icon
className="file-upload-ordinal-help-icon"
inverted
size="large"
color="orange"
name="info circle"
/>
}
/>
</Accordion.Title>
<Accordion.Content
active={activeAccordionIndex === 0}
>
<textarea
className="file-upload-ordinal-text-area"
id="ordinal_features_text_area_input"
label="Ordinal Features"
placeholder={"{\"ord_feat_1\": [\"MALE\", \"FEMALE\"], \"ord_feat_2\": [\"FIRST\", \"SECOND\", \"THIRD\"]}"}
onChange={this.handleOrdinalFeatures}
/>
</Accordion.Content>
</Accordion>
)
return accordionContent;
}
}
I don't know how to set this up to allow multiple accordions open at once with content that is not a string. Is this possible with the semantic ui accordion? Or do I need to find an alternative solution and/or make the piece with the desired behavior by hand?
You can change your index logic so instead of setting the active index in your state add the index to an array and check if it exists inside the array and if it does show that accordion
Here is an example:
export default class AccordionExampleStandard extends Component {
state = { activeIndexs: [] };
handleClick = (e, titleProps) => {
const { index } = titleProps;
const { activeIndexs } = this.state;
const newIndex = activeIndexs;
const currentIndexPosition = activeIndexs.indexOf(index);
if (currentIndexPosition > -1) {
newIndex.splice(currentIndexPosition, 1);
} else {
newIndex.push(index);
}
this.setState({ activeIndexs: newIndex });
};
render() {
const { activeIndexs } = this.state;
return (
<Accordion>
<Accordion.Title
active={activeIndexs.includes(0)}
index={0}
onClick={this.handleClick}
>
<Icon name="dropdown" />
What is a dog?
</Accordion.Title>
<Accordion.Content active={activeIndexs.includes(0)}>
<p>
A dog is a type of domesticated animal. Known for its loyalty and
faithfulness, it can be found as a welcome guest in many households
across the world.
</p>
</Accordion.Content>
<Accordion.Title
active={activeIndexs.includes(1)}
index={1}
onClick={this.handleClick}
>
<Icon name="dropdown" />
What kinds of dogs are there?
</Accordion.Title>
<Accordion.Content active={activeIndexs.includes(1)}>
<p>
There are many breeds of dogs. Each breed varies in size and
temperament. Owners often select a breed of dog that they find to be
compatible with their own lifestyle and desires from a companion.
</p>
</Accordion.Content>
<Accordion.Title
active={activeIndexs.includes(2)}
index={2}
onClick={this.handleClick}
>
<Icon name="dropdown" />
How do you acquire a dog?
</Accordion.Title>
<Accordion.Content active={activeIndexs.includes(2)}>
<p>
Three common ways for a prospective owner to acquire a dog is from
pet shops, private owners, or shelters.
</p>
<p>
A pet shop may be the most convenient way to buy a dog. Buying a dog
from a private owner allows you to assess the pedigree and
upbringing of your dog before choosing to take it home. Lastly,
finding your dog from a shelter, helps give a good home to a dog who
may not find one so readily.
</p>
</Accordion.Content>
</Accordion>
);
}
}
https://codesandbox.io/s/xo226wp5lw?module=example.js
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.