React newbie here, I am having problems with my TinySlider component. Each time I update in the UI how many posts can appear in the carousel, I get this error every other update which I need to fix:
Uncaught (in promise) TypeError: Cannot read property 'appendChild' of null
If I remove <TinySlider settings={...settings}></Tinyslider> I do not get this error.
I have tried this: { renderProfilesCarousel ? renderProfilesCarousel : '' } inside the <tinySlider> but that does not work.
Any idea what I could do here? Pretty stuck on this now.
// React
import * as React from 'react';
// Styling
import styles from './LinkedIn.module.scss';
// Importing the props
import { ILinkedInProps } from './ILinkedInProps';
// Importing the state
import { ILinkedInState } from './ILinkedInState';
// Removes special characters
import { escape } from '#microsoft/sp-lodash-subset';
// Library for making http requests
import axios from 'axios';
// Library for creating unique ids
import shortid from 'shortid';
// Fabric UI elements
import {
DocumentCard,
DocumentCardPreview,
DocumentCardType,
DocumentCardDetails,
DocumentCardTitle,
IDocumentCardPreviewProps
} from 'office-ui-fabric-react/lib/DocumentCard';
import { ImageFit, Image } from 'office-ui-fabric-react/lib/Image';
// Sort array
import sortBy from 'sort-array';
import TinySlider from "tiny-slider-react";
import { SPComponentLoader } from '#microsoft/sp-loader';
import "./styles.scss";
// LinkedIn Component Class
export default class LinkedIn extends React.Component<ILinkedInProps, ILinkedInState> {
// State needed for the component
constructor(props) {
super(props);
this.state = {
profiles: [],
isLoading: true,
errors: null
};
SPComponentLoader.loadCss('//cdnjs.cloudflare.com/ajax/libs/tiny-slider/2.9.2/tiny-slider.css');
}
// This function runs when component is first renderd
public componentDidMount() {
this.getProfiles();
}
// This function runs when props that have changed have been passed in
public componentDidUpdate(prevProps) {
if ( prevProps.listName !== this.props.listName || prevProps.postCount ! == this.props.postCount )
{
this.renderProfile();
}
}
// Grabs LinkedIn profiles - This service runs once a day
private getProfiles() {
let companyNameCreate: string;
let backUpImageCreate: string;
axios
.get(
"https://cors-anywhere-spfx.herokuapp.com/" +
"https://cache1.phantombooster.com/YRrbtT9qhg0/KJhwG7zo0zPE5zc9Eehn6Q/result.json"
)
.then(response => {
this.setState({
profiles: response.data.filter(d => d.postContent).map(post => {
if (post.profileUrl == 'https://www.linkedin.com/company/')
{
companyNameCreate = 'company';
backUpImageCreate = 'https://media-exp2.licdn.com/dms/image/C4D0BAQEbfV4VNvsJyg/company-logo_100_100/0?e=1587600000&v=beta&t=CX_s-ekYNn0TnXANeftQkLZ9jIvMW7PtDTLLcHcu9wY'
}
else if (post.profileUrl == 'https://www.linkedin.com/company/1')
{
companyNameCreate = 'company';
backUpImageCreate = 'https://media-exp2.licdn.com/dms/image/C4D0BAQG_Pr1cDaGfdA/company-logo_200_200/0?e=1587600000&v=beta&t=C0fWkjbO2Elth8K4pG4i_kzwlu8dvQvN1Ws-yKGxxP4'
}
else if (post.profileUrl == 'https://www.linkedin.com/company/2')
{
companyNameCreate = 'company';
backUpImageCreate = 'https://media-exp2.licdn.com/dms/image/C4D0BAQHdub-mnNwSNg/company-logo_100_100/0?e=1587600000&v=beta&t=druqo_O5gB5cExttREUlSdGnJhr4Wx2-PCjshJ0K0fI'
}
else if (post.profileUrl == 'https://www.linkedin.com/company/3')
{
companyNameCreate = 'company';
backUpImageCreate = 'https://media-exp2.licdn.com/dms/image/C4D0BAQEUKGn5i1EnQA/company-logo_100_100/0?e=1587600000&v=beta&t=uwE3CUaodiqmW2K3a3Hm57QDIDlMvrmfmoHDvlGnzuY'
}
else if (post.profileUrl =='https://www.linkedin.com/company/4')
{
companyNameCreate = 'company';
backUpImageCreate = 'https://media-exp2.licdn.com/dms/image/C4D0BAQGYqqxF43Rfdg/company-logo_200_200/0?e=1587600000&v=beta&t=4hmLzdbkjk_hL3rwonWgTbUF1V-itkyBEfLBh85G7_k'
}
else if (post.profileUrl == 'https://www.linkedin.com/company/5')
{
companyNameCreate = 'company';
backUpImageCreate = 'https://media-exp2.licdn.com/dms/image/C4E0BAQHsNKLawvW7zg/company-logo_100_100/0?e=1587600000&v=beta&t=26Otro4T3q90GznPxXX6n3oPTYYWIgzodOIask0enu4'
}
return {
...post,
companyName: companyNameCreate,
backUpImage: backUpImageCreate
}
})
});
})
// Error catching
.catch(errors => this.setState({ errors, isLoading: false }));
}
// Creates the renderd layout of the LinkedIn profile
private async renderProfile() {
let filter: string = '';
// Works out which profile to display
switch (this.props.listName) {
case "abfi":
filter = 'https://www.linkedin.com/company/1';
break;
case 'abEnzymes':
filter = 'https://www.linkedin.com/company/2';
break;
case 'abitec':
filter = 'https://www.linkedin.com/company/3';
break;
case 'ohly':
filter = 'https://www.linkedin.com/company/4';
break;
case 'pgpi':
filter = 'https://www.linkedin.com/company/5';
break;
case 'spiPharma':
filter = 'https://www.linkedin.com/company/6';
break;
case 'all':
filter = 'Post';
break;
default:
filter = 'https://www.linkedin.com/company/1';
return filter;
}
// Grabs the array of objects
let { profiles } = this.state;
const slotOrder = [
"", "1h","2h","3h","4h","5h","6h","7h","8h","9h","10h","11h","12h", "13h","14h","15h","16h","17h","18h","19h","20h","21h","22h", "23h", "2d", "3d", "4d", "5d", "6d", "1w", "2w", "3w", "1mo", "2mo", "3mo", "4mo", "5mo", "6mo", "7mo", "8mo", "9mo", "10mo", "11mo", "1yr", "2yr"
];
// Select only the needed objects from the array
let selectedProfile;
// Display all posts
if (this.props.listName !== 'all') {
selectedProfile = profiles.filter(profile => profile.profileUrl == filter);
} else {
selectedProfile = profiles.filter(profile => profile.action == filter);
}
selectedProfile = sortBy(selectedProfile, "postDate", { postDate: slotOrder })
selectedProfile = selectedProfile.splice(0, this.props.postCount)
// Renders the selected profile
let renderProfilesCarousel = selectedProfile.map(profile => {
// If LinkedIn post has no image, then add a fallback!
if (profile.imgUrl == "" || profile.imgUrl == null ) {
profile.imgUrl = profile.backUpImage;
}
// Removes hashtag line from posts
profile.postContent = profile.postContent.replace(/hashtag/g, '');
return(
<div className={styles.linkedInContainerCarousel} style={{ position: "relative" }} key={shortid.generate()}>
<a href={profile.postUrl} target="_blank" data-interception="off">
<DocumentCard
aria-label={profile.postContent}
className={styles.linkedInDocCard}
>
{ profile.imgUrl &&
<Image
src={profile.imgUrl}
imageFit={ImageFit.cover}
height={168}
/>
}
<DocumentCardTitle
title={profile.postContent}
shouldTruncate={true}
/>
<p className={ styles.linkedInCompany}>{profile.companyName}</p>
<p className={ styles.linkedInLikes}>{`Likes: ${profile.likeCount}`}</p>
</DocumentCard>
</a>
</div>
)
});
// Renders the selected profile
let renderProfiles = selectedProfile.map(profile => {
// If LinkedIn post has no image, then add a fallback!
if (profile.imgUrl == "" || profile.imgUrl == null ) {
profile.imgUrl = profile.backUpImage;
}
let previewProps: IDocumentCardPreviewProps = {
previewImages: [
{
name: profile.postContent,
previewImageSrc: profile.imgUrl,
iconSrc: null,
imageFit: ImageFit.cover,
height: 110,
width: 110
}
]
};
return(
<div className={styles.linkedInContainer} style={{ position: "relative" }} key={shortid.generate()}>
<a href={profile.postUrl} target="_blank" data-interception="off">
<DocumentCard
aria-label={profile.postContent}
className={styles.linkedInDocCard}
type={DocumentCardType.compact}
>
{ profile.imgUrl &&
<DocumentCardPreview {...previewProps} />
}
<DocumentCardDetails>
<DocumentCardTitle
title={profile.postContent}
shouldTruncate={true}
/>
<p className={ styles.linkedInCompany}>{profile.companyName}</p>
<p className={ styles.linkedInLikes}>{`Likes: ${profile.likeCount}`}</p>
</DocumentCardDetails>
</DocumentCard>
</a>
</div>
)
});
let settings: any;
if (this.props.toggleInfoHeaderValue == true )
{
settings = {
lazyload: true,
nav: false,
mouseDrag: false,
items: 1,
swipeAngle: false,
speed: 400,
autoplay: false,
axis: "horizontal",
autoHeight: false,
rewind: true,
fixedWidth: false
};
}
else
{
settings = {
lazyload: true,
nav: false,
mouseDrag: false,
items: 3,
swipeAngle: false,
speed: 400,
autoplay: false,
axis: "vertical",
autoHeight: false,
rewind: true,
fixedWidth: false
};
};
if (this.props.toggleInfoScrollValue) {
settings.autoplay = true;
} else {
settings.autoplay = false;
}
if (this.props.toggleInfoHeaderValue == true ) {
return(
<div>
<TinySlider settings={settings}>
{renderProfilesCarousel}
</TinySlider>
</div>
)
} else {
return (
<div className={styles.upArrows}>
<TinySlider settings={settings}>
{renderProfiles}
</TinySlider>
</div>
)
}
}
// Renders to the browser
public render(): React.ReactElement<ILinkedInProps> {
return (
<div className={ styles.linkedIn }>
<div className={ styles.container }>
<p className={ styles.title }>{escape(this.props.description)}</p>
<div>
{ this.renderProfile() }
</div>
</div>
</div>
);
}
}
Error in full:
Counld you try this instead
{ renderProfilesCarousel ? renderProfilesCarousel : <span></span> }
React likes it when it has elements, and I'm not sure how it'd deal with a ''
Edit to edit:
I think you'll want to move the actual JSX.Element out of the renderProfile() method. React doesn't take that as a child element.
So I added two new items to state (I guess you'll want three, one for renderProfilesCarousel too):
settings?: any;
renderProfiles?: JSX.Element[];
Then I did this at the bottom of the renderProfile() method:
/* if (this.props.toggleInfoHeaderValue == true) {
return (
<div>
<TinySlider settings={settings}>
{renderProfilesCarousel}
</TinySlider>
</div>
)
} else {
return (
<div /* className={styles.upArrows} >
<TinySlider settings={settings}>
{renderProfiles}
</TinySlider>
</div>
)
} */
console.log(renderProfiles.length);
this.setState({
settings: settings,
renderProfiles: renderProfiles,
isLoading: false
})
Then, in your return of the actual render to the browser is where I put the actual JSX.Element:
// Renders to the browser
public render(): React.ReactElement<ILinkedInProfilesProps> {
const {settings, renderProfiles} = this.state;
const theRenderProfileJsxElement: JSX.Element =
<div /* className={styles.upArrows} */>
<TinySlider settings={settings}>
{renderProfiles}
</TinySlider>
</div>;
return (
<div /* className={styles.linkedIn} */>
<div /* className={styles.container} */>
<p /* className={styles.title} */>{escape(this.props.description)}</p>
<div>
{this.state.isLoading === false &&
theRenderProfileJsxElement
}
</div>
</div>
</div>
);
}
And I used your state of isLoading to prevent the Carousel from loading prior to finishing all the logic and loading from above.
Also! If you don't have React Dev Tools on your browser, you need it!
I can see the component Carousel, but I didn't do the if logic for either the toggleInfoHeaderValue. Let's see if that works?
Related
I am using MERN stack and Redux. I have created an array in the state 'comments' which is updated via the clickHandler function with elements from the global state (accessed via props). When i try to show the contents of the array in the render i just get the length of it. How would i show the properties of the elements for example title.
import React, { Component } from "react";
import PropTypes from "prop-types";
import GoogleSearch from "./GoogleSearch";
import { connect } from "react-redux";
import { fetchSubjects } from "../../actions/subject";
import { fetchComments } from "../../actions/comment";
import store from "../../store";
class Subject extends Component {
// on loading the subjects and comments
// are fetched from the database
componentDidMount() {
this.props.fetchSubjects();
this.props.fetchComments();
}
constructor(props) {
super(props);
this.state = {
// set inital state for subjects description
// and summary to invisible
viewDesription: -1,
viewSummary: -1,
comments: [],
};
}
componentWillReceiveProps(nextProps) {
// new subject and comments are added to the top
if (nextProps.newPost) {
this.props.subjects.unshift(nextProps.newPost);
}
if (nextProps.newPost) {
this.props.comments.unshift(nextProps.newPost);
}
}
clickHandler = (id) => {
// when a subject title is clicked pass in its id
// and make the desciption visible
const { viewDescription } = this.state;
this.setState({ viewDescription: viewDescription === id ? -1 : id });
// clear the existing comments in state
this.setState({
comments: [],
});
// loop through the comment items in the global state
// and add any with the same subjects id passed in to the array
var i;
for (i = 0; i < this.props.comments.length; i++) {
if (this.props.comments[i].subject == id) {
console.log(this.props.comments[i]);
this.setState({
comments: this.state.comments.unshift(this.props.comments[i]),
});
}
} // set local storage to the id for the subject that has been clicked
localStorage.setItem("passedSubject", id);
};
// hovering on and off subjects toggles the visibility of the summary
hoverHandler = (id) => {
this.setState({ viewSummary: id });
};
hoverOffHandler = () => {
this.setState({ viewSummary: -1 });
};
render() {
const subjectItems = this.props.subjects.map((subject) => {
// if the state equals the id set to visible if not set to invisible
var view = this.state.viewDescription === subject._id ? "" : "none";
var hover = this.state.viewSummary === subject._id ? "" : "none";
var comments = this.state.comments;
return (
<div key={subject._id}>
<div
className="subjectTitle"
onClick={() => this.clickHandler(subject._id)}
onMouseEnter={() => this.hoverHandler(subject._id)}
onMouseLeave={() => this.hoverOffHandler()}
>
<p className="title">{subject.title}</p>
<p className="rating">Rating: {subject.rating}</p>
<p className="summary" style={{ display: hover }}>
{subject.summary}
</p>
</div>
<div className="subjectBody " style={{ display: view }}>
<div className="subjectAuthor">
<p className="author">
Subject created by: {subject.author} on {subject.date}
</p>
<a href="">
<div className="buttonRateSubject">RATE SUBJECT</div>
</a>
</div>
<div className="subjectDescription">
<p className="description">{subject.description}</p>
</div>
<div className="subjectLinks">Links:</div>
<div className="subjectComments">
<p>Comments:</p>
{/* ************HERE*********** */}
<p>{comments}</p>
{/* ********************************* */}
<a href="/addcomment">
<div className="buttonAddComment">ADD COMMENT</div>
</a>
</div>
</div>
</div>
);
});
return (
<div id="Subject">
<GoogleSearch />
{subjectItems}
</div>
);
}
}
Subject.propTypes = {
fetchSubjects: PropTypes.func.isRequired,
fetchComments: PropTypes.func.isRequired,
subjects: PropTypes.array.isRequired,
comments: PropTypes.array.isRequired,
newPost: PropTypes.object,
};
const mapStateToProps = (state) => ({
subjects: state.subjects.items,
newSubject: state.subjects.item,
comments: state.comments.items,
newComment: state.comments.item,
});
// export default Subject;
export default connect(mapStateToProps, { fetchSubjects, fetchComments })(
Subject,
Comment
);
I think I know your problem. You want to render items of an array.
Let me just give you a short overview.
Javascript:
this.setState({
comments: data
});
render (){
return (
<div>
{ this.state.comments.map(c=> <div>{c.body}</div> ) }
</div>
)
}
Thanks guys, i changed the for loop in the clickHandler to this which now has data rendering, it didn't like objects in the array for some reason.
var temp = [];
for (i = 0; i < this.props.comments.length; i++) {
if (this.props.comments[i].subject == id) {
console.log(this.props.comments[i]);
temp.unshift(this.props.comments[i].comment);
temp.unshift(this.props.comments[i].title);
}
}
this.setState({
comments: temp,
});
Why does ReactJS remove the last element when the array is different after removing the middle element when using array.splice?
This is my code. I am using React-Redux.
const reducerNotesAndLogin = (state = initialState, action) => {
var tableNotes = "notities";
var tableCategories = "categories";
switch(action.type){
case "CATEGORY_REMOVE":
// Remove the category
var newCategories = state.categories;
console.log("state.categories", state.categories);
console.log("before: ", {newCategories});
var index = 0;
for(var i = 0; i < newCategories.length; i++){
if(newCategories[i].id === action.payload.categoryId){
newCategories.splice(i, 1);
index = i;
i--;
}
}
console.log("after: ", {newCategories});
state = {
...state,
categories: newCategories
}
break;
default:
break;
}
return state;
}
export default reducerNotesAndLogin;
Output below (I deleted the middle element. My web app always removes the last element of the categories (but not from the array).
Step 1: Initial state
Step 2: Remove middle item, expecting the middle item to be removed.
Step 3: Confusion
Why is the array correct, but the view incorrect? I am updating the state.categories correctly right?
This is my render code (as is - without filtering away any other code that mihgt be important)
CategoriesBody:
import React from 'react';
import { connect } from 'react-redux';
import CategoryItem from './CategoryItem';
import Button from './../../Button';
import store from '../../../redux/store-index';
class CategoriesBody extends React.Component {
render(){
return (
<div>
<ul className="list--notes">
{this.props.categories.map((category) => {
if(category.id === undefined){ // No categories
return <li>No categories</li>
} else {
return (
<div>
<CategoryItem category={category} />
<div className="mb-small hidden-sm hidden-md hidden-lg"> </div>
</div>
);
}
})}
</ul>
</div>
);
}
}
function mapStateToProps(state){
return {
categories: state.reducerNotesAndLogin.categories,
categoriesLength: state.reducerNotesAndLogin.categories.length
};
}
export default connect(mapStateToProps)(CategoriesBody);
CategoriesItem.js:
import React from 'react';
import store from './../../../redux/store-index';
import Button from './../../Button';
class CategoryItem extends React.Component {
constructor(props){
super();
this.state = {
edit: false,
categoryName: props.category.categoryName,
categoryColor: props.category.categoryColor
}
this.onClickEdit = this.onClickEdit.bind(this);
this.onChangeCategoryColor = this.onChangeCategoryColor.bind(this);
this.onChangeInputCategoryName = this.onChangeInputCategoryName.bind(this);
this.onClickEditSave = this.onClickEditSave.bind(this);
this.onClickEditCancel = this.onClickEditCancel.bind(this);
}
removeCategory(id, name){
console.log("nsvbsvbfjvbdjhbvv");
store.dispatch({ type: "CATEGORY_REMOVE", payload: {
categoryId: id
}});
// store.dispatch({type: "NOTIFY", payload: {
// type: 'success',
// message: 'Category "' + name + '" removed!'
// }});
}
onClickEdit(){
this.setState({
edit: true
});
}
onChangeCategoryColor(e){
this.setState({
categoryColor: e.target.value
});
}
onChangeInputCategoryName(e){
this.setState({
categoryName: e.target.value
});
}
onClickEditSave(){
this.setState({
edit: false,
categoryName: this.state.categoryName,
categoryColor: this.state.categoryColor
});
store.dispatch({type: "CATEGORY_EDIT", payload: {
categoryId: this.props.category.id,
categoryName: this.state.categoryName,
categoryColor: this.state.categoryColor
}});
store.dispatch({type: "NOTIFY", payload: {
type: "success",
message: "Category saved!"
}});
}
onClickEditCancel(){
this.setState({
edit: false,
categoryName: this.props.category.categoryName,
categoryColor: this.props.category.categoryColor
});
}
render(){
return (
<li key={this.props.category.id} className={this.state.edit === true ? "mt mb" : "flex-justify-between flex-align-center"}>
<div className={this.state.edit === true ? "d-none" : ""}>
<div className="input--color" style={{
backgroundColor: this.state.categoryColor
}}> </div>
{this.state.categoryName}
</div>
{/* Mobile */}
<div className={this.state.edit === true ? "d-none" : "hidden-sm hidden-md hidden-lg"}>
<Button onClick={() => this.onClickEdit()} buttonType="primary">Edit</Button>
<div className="mt-small"> </div>
<Button onClick={() => this.removeCategory(this.props.category.id, this.props.category.categoryName)} type="primary">Remove</Button>
</div>
{/* Tablet and desktop */}
<div className={this.state.edit === true ? "d-none" : "hidden-xs"}>
<div style={{float:'left',}}><Button onClick={() => this.onClickEdit()} buttonType="primary">Edit</Button></div>
<div style={{float:'left',marginLeft:'15px'}}><Button onClick={() => this.removeCategory(this.props.category.id, this.props.category.categoryName)} type="primary">Remove</Button></div>
</div>
{/* EDITING STATE */}
<div className={this.state.edit === true ? "" : "d-none"}>
<div className="row">
<div className="col-xs-12">
<input onChange={this.onChangeCategoryColor} className="input--wide" type="color" value={this.state.categoryColor}
style={{backgroundColor: this.state.categoryColor, height: '30px'}}
/>
<input onChange={this.onChangeInputCategoryName} className="input--wide" type="text" value={this.state.categoryName} />
</div>
</div>
<div className="row mt">
<div className="col-xs-12">
<Button buttonType="primary" onClick={() => this.onClickEditSave()}>Save</Button>
</div>
</div>
<div className="row mt-small">
<div className="col-xs-12">
<Button buttonType="secondary" onClick={() => this.onClickEditCancel()}>Cancel</Button>
</div>
</div>
</div>
</li>
)
}
}
export default CategoryItem;
I think it has something to do with the rendering. Because the arrays are correct when I console.log them. Only the view is different...
Do not modify the state in reducer directly. Create a copy of state value and then modify it.
Change:
var newCategories = state.categories;
To:
var newCategories = [...state.categories];
You should not modify the same array while looping through it.
for (var i = 0; i < newCategories.length; i++) {
if (newCategories[i].id === action.payload.categoryId) {
newCategories.splice(i, 1);
index = i;
i--;
}
}
I got the answer after looking through it with a friend of mine. The solution is pretty simple...
Lesson 101: Make sure that you have a unique "key" property when looping through an array in your UI.
The solution is to add this to my code:
<div key={category.id}>
{this.props.categories.map....
...
</div>
I'm building a react application, and on my HomePage, I have the component 'CategoriesList'.
When I'm on the HomePage, the 'Categories List' works well, but when I navigated to ProductDetails page with react-router-dom, I found this error:
"NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node."
'CategoriesList' uses Flickty. I tried to remove Flickity, and... Works well. But I need to use Flickity.
Can anyone help me?
CategoryList Component:
const CategoryList = ({list, popupOpen, refreshProductList}) => {
return (
<Container>
<Slider
options={{
cellAlign: 'center',
draggable: true,
groupCells: true,
contain: false,
pageDots: false,
}}
style={ popupOpen ? ({opacity: 0.05}) : null}
>
{list.map((category, index) => (
<Category key={index}>{category}</Category>
))}
</Slider>
</Container>
);
}
Flickty Slider Component:
import Flickity from 'flickity';
import 'flickity/dist/flickity.min.css';
export default class Slider extends Component {
constructor(props) {
super(props);
this.state = {
flickityReady: false,
};
this.refreshFlickity = this.refreshFlickity.bind(this);
}
componentDidMount() {
this.flickity = new Flickity(this.flickityNode, this.props.options || {});
this.setState({
flickityReady: true,
});
}
refreshFlickity() {
this.flickity.reloadCells();
this.flickity.resize();
this.flickity.updateDraggable();
}
componentWillUnmount() {
this.flickity.destroy();
}
componentDidUpdate(prevProps, prevState) {
const flickityDidBecomeActive = !prevState.flickityReady && this.state.flickityReady;
const childrenDidChange = prevProps.children.length !== this.props.children.length;
if (flickityDidBecomeActive || childrenDidChange) {
this.refreshFlickity();
}
}
renderPortal() {
if (!this.flickityNode) {
return null;
}
const mountNode = this.flickityNode.querySelector('.flickity-slider');
if (mountNode) {
return ReactDOM.createPortal(this.props.children, mountNode);
}
}
render() {
return [
<div style={this.props.style} className={'test'} key="flickityBase" ref={node => (this.flickityNode = node)} />,
this.renderPortal(),
].filter(Boolean);
}
}
If you want to use Flickity along with React instead of creating your own component, I highly recommend using react-flickity-component. It's also easy to use:
Tip: if you use wrapAround option in Flickity set
disableImagesLoaded prop ture (default is false).
import Flickity from 'react-flickity-component'
const flickityOptions = {
autoPlay: 4000,
wrapAround: true
}
function Carousel() {
return (
<Flickity
disableImagesLoaded
options={flickityOptions} // Takes flickity options {}
>
<img src="/images/placeholder.png"/>
<img src="/images/placeholder.png"/>
<img src="/images/placeholder.png"/>
</Flickity>
)
}
I'm follow the steps of this dependencie:
http://jossmac.github.io/react-images/
And it isn't work. No picture showing and there is showing an error message:
index.js:2178 Warning: Failed prop type: The prop onClose is marked
as required in Lightbox, but its value is undefined
Here is my code:
import React, { Component } from "react";
import Lightbox from "react-images";
const URL_INTERIORES = "http://localhost:3001/interiores";
const LIGHTBOX_IMAGE_SET = [
{
src: "/images/int_02.jpg",
caption: "A forest",
// As an array
srcSet: ["/images/int_02.jpg", "/images/int_02.jpg"]
},
{
src: "/images/int_02.jpg",
// As a string
srcSet: "/images/int_02.jpg 1024w, /images/int_02.jpg 800w, /images/int_02.jpg 500w, /images/int_02.jpg 320w"
}
];
class Interiores extends Component {
render() {
const { open } = this.state;
return (
<div>
<div>
<Lightbox
images={LIGHTBOX_IMAGE_SET}
isOpen={this.state.lightboxIsOpen}
onClickPrev={this.gotoPrevLightboxImage}
onClickNext={this.gotoNextLightboxImage}
onClose={this.closeLightbox}
/>
</div>
</div>
);
}
}
export default Interiores;
Does anybody know how to solve it? Tahnk you
Consider adding all the missing handlers & state in your class:
class Interiores extends Component {
state = {
lightboxIsOpen: false
}
gotoPrevLightboxImage() {
// Add the logic here
}
gotoNextLightboxImage() {
// Add the logic here
}
closeLightbox(e) {
// Add the logic here
}
render() {
const { lightboxIsOpen } = this.state;
return (
<div>
<Lightbox
images={LIGHTBOX_IMAGE_SET}
isOpen={lightboxIsOpen}
onClickPrev={() => this.gotoPrevLightboxImage()}
onClickNext={() => this.gotoNextLightboxImage()}
onClose={e => this.closeLightbox(e)}
/>
</div>
);
}
}
I want to render buttons in react-bootstrap-table. However, If I pass a React component as the content, the table is render with [object Object].
Here's the code I've tried so far:
// Imports
import React, { Component } from "react";
import { Link } from "react-router-dom";
import { BootstrapTable, TableHeaderColumn } from "react-bootstrap-table";
import "../../../node_modules/react-bootstrap-table/css/react-bootstrap-table.css";
// Exports
export default class Table extends Component {
constructor(props) {
super(props);
// Defaults
this.props.options.search = this.props.options.search ? true : false;
this.props.options.pagination = this.props.options.pagination ? true : false;
}
// Option Buttons
optionButtons = (cell, row) => {
return cell.map(item => {
let key = Object.keys(item)[0];
return (
<Link to={item[key]} className="btn btn-sm">
{key}
</Link>
);
});
};
// This works however
// optionButtons = (cell, row) => {
// return <Link to="/some/route" className="btn btn-sm">Action</Link>;
// };
render() {
let headings = this.props.columns.map((heading, index) => {
let key = Object.keys(heading)[0];
if (index === 0) {
return (
<TableHeaderColumn
key={heading[key]}
dataSort={heading.sortable ? true : false}
dataField={key}
width={
heading.width && !isNaN(heading.width)
? heading.width.toString()
: null
}
isKey
>
{heading[key]}
</TableHeaderColumn>
);
}
if (key === "options") {
return (
<TableHeaderColumn
key={heading[key]}
dataFormat={this.optionButtons}
dataField={key}
width={
heading.width && !isNaN(heading.width)
? heading.width.toString()
: null
}
>
{heading[key]}
</TableHeaderColumn>
);
}
return (
<TableHeaderColumn
key={heading[key]}
dataSort={heading.sortable ? true : false}
dataField={key}
width={
heading.width && !isNaN(heading.width)
? heading.width.toString()
: null
}
>
{heading[key]}
</TableHeaderColumn>
);
});
return (
<BootstrapTable
data={this.props.data}
search={this.props.options.search}
pagination={this.props.options.pagination}
>
{headings}
</BootstrapTable>
);
}
}
The data I am passing to the options key is an array with multiple objects. The problem is in rendering the option buttons. The idea is to be able to pass the number of buttons/link I want from a component and they will be rendered.
This is the data being passed to the options key:
options: [
{ Edit: `/item/${item.id}/edit` },
{ Delete: `/item/${item.id}/delete` }
]
Looks like dataFormat expects a single value, wrap your buttons into a root element (div for example), or into a fragment if supported.