React-elastic-carousel only functional after resizing window - javascript

I've just started using the react-elastic-carousel package and am running into an issue on page load. Everything loads up on page load/refresh, but when I click the arrow to move the carousel, the items aren't rotating. Only upon resizing the window do the products re-format correctly, and then I can continue to use the buttons normally from then on. I've commented out all of my CSS thinking that it was maybe clashing with the package's CSS but it did nothing. I know that this package uses Resize Observer, but am not sure if that would be the issue or not. I've attached my code for the component that's using the carousel as well as a link to the Github of the react-elastic-carousel package. Any direction or advice is appreciated!
import React from 'react';
import axios from 'axios';
import RelatedProducts from './RelatedProducts.jsx';
import OutfitList from './OutfitList.jsx';
import Carousel from "react-elastic-carousel";
//Custom styles for carousel//
const breakPoints = [
{ width: 1, itemsToShow: 1 },
{ width: 550, itemsToShow: 2 },
{ width: 768, itemsToShow: 3 },
{ width: 1200, itemsToShow: 4 },
];
class RelatedProductsList extends React.Component {
constructor(props) {
super(props);
this.state = {
products: []
}
this.getRelated = this.getRelated.bind(this);
}
componentDidUpdate(prevProps) {
if (prevProps.mainProduct.id !== this.props.mainProduct.id) {
this.getRelated()
}
}
getRelated() {
axios.get(`/api/${this.props.mainProduct.id}`)
.then((results) => {
this.setState({
products: results.data
})})
.catch((err) => console.log(err));
};
render() {
return (
<div>
<Carousel breakPoints={breakPoints}>
{this.state.products.map((id, index) => {
return (
<RelatedProducts productId={id} key={index} mainProduct={this.props.mainProduct} updateCurrentProduct={this.props.updateCurrentProduct}/>
)
})}
</Carousel>
</div>
);
}
};
export default RelatedProductsList;
Github of the carousel package

Related

what is the best way to setParams to navigation before setting the options for Navigation using setOptions?

I wanna know the best way to set the params and options for react native navigation in a class component.
note that the same params are used in options.
when I put all code in the constructor I got params undefined because of timing issue.
and it works. for me in one case when I added option in componentDidMount , I will write some examples in the code below.
1- first case using class component (it's working)
type Props = {
navigation: NavigationProp<any>;
route: RouteProps<{ Example: {title: string} }, 'Example'>
}
export default class Example extends Component <Props> {
constructor(props: Props){
super(props)
this.props.navigation.setParams({ title: 'title' });
}
componentDidMount(){
this.props.navigation.setOptions({ title: this.props.route.params.title })
}
...
}
2 - second case using FC: (not using this example but I think it's also the best way todo for the FC).
export function Example: React.FC = () => {
const navigation = useNavigation();
const route = useRoute();
useLayoutEffect(()=>{
navigation.setParams({ title: 'title' });
navigation.setOptions({ title: route.params.title })
})
...
}
so I hope my question is clear, is that theright way to set Header options with the lates Navigation on React Native?
constructor is the first step in component lifecycle, and you are setting params inside that, which means there is a prop that is going to be updated.
so we need a function that understands every update on a state or received props, and that listener is nothing except "componentDidUpdate(){}" 🤟:
import {NavigationProp, RouteProp} from '#react-navigation/native';
import React, {Component} from 'react';
import {Text, StyleSheet, View} from 'react-native';
type Props = {
navigation: NavigationProp<any>;
route: RouteProp<{Example: {title: string}}, 'Example'>;
};
export default class Example extends Component<Props> {
constructor(props: Props) {
super(props);
this.props.navigation.setParams({title: 'title'});
}
componentDidUpdate() {
this.props.navigation.setOptions({title: this.props.route.params.title});
}
render() {
return (
<View style={styles.container}>
<Text style={styles.textStyle}>Use component did update :)</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
padding: 30,
},
textStyle: {
color: 'black',
fontSize: 20,
fontWeight: 'bold',
},
});

EditorJS in NextJS not able to load plugins

I am trying to get EditorJS working in NextJS. The editor loads fine without plugins, having the only paragraph as a block option. However, when I attempt to add plugins via tools prop console throws the following warning:
editor.js?9336:2 Module Tools was skipped because of TypeError: Cannot read property 'prepare' of undefined
When I click on the editor in the browser, it is throwing:
Uncaught TypeError: Cannot read property 'holder' of undefined
I have tested editor plugins in the normal React app, and they load fine. Meaning that the problem is in EditorJS and NextJS import and handling of plugins. I have tried to import editor and plugins in componentDidMount hook using require but had the same problem as with NextJS dynamic imports. Attempted to get component using React ref but found that currently NextJS has problems with getting components' refs, Tried suggested workaround but still had no result. The instance of the editor is not available until onChange is triggered, so plugins just cannot hook into the editor due to that 'prepare' property or the whole editor are being undefined until an event on editor has happened, but the editor outputs into the console that it is ready.
My component's code:
import React from "react";
import dynamic from "next/dynamic";
const EditorNoSSR = dynamic(() => import("react-editor-js"), { ssr: false });
const Embed = dynamic(() => import("#editorjs/embed"), { ssr: false });
class Editor extends React.Component {
state = {
editorContent: {
blocks: [
{
data: {
text: "Test text",
},
type: "paragraph",
},
],
},
};
constructor(props) {
super(props);
this.editorRef = React.createRef();
}
componentDidMount() {
console.log(this.editorRef.current);
console.log(this.editorInstance);
}
onEdit(api, newData) {
console.log(this.editorRef.current);
console.log(this.editorInstance);
this.setState({ editorContent: newData });
}
render() {
return (
<EditorNoSSR
data={this.state.editorContent}
onChange={(api, newData) => this.onEdit(api, newData)}
tools={{ embed: Embed }}
ref={(el) => {
this.editorRef = el;
}}
instanceRef={(instance) => (this.editorInstance = instance)}
/>
);
}
}
export default Editor;
Is there any solution to this problem? I know SSR is challenging with client side rendering of components that access DOM, but there was condition used that checked whether window object is undefined, however, it does not look like an issue in my situation.
UPDATE:
I have found a solution but it is rather not a NextJS way of solving the problem, however, it works. It does not require a react-editorjs and implemented as creation of EditorJS instance as with normal EditorJS.
class Editor extends React.Component {
constructor(props) {
super(props);
this.editor = null;
}
async componentDidMount() {
this.initEditor();
}
initEditor = () => {
const EditorJS = require("#editorjs/editorjs");
const Header = require("#editorjs/header");
const Embed = require("#editorjs/embed");
const Delimiter = require("#editorjs/delimiter");
const List = require("#editorjs/list");
const InlineCode = require("#editorjs/inline-code");
const Table = require("#editorjs/table");
const Quote = require("#editorjs/quote");
const Code = require("#editorjs/code");
const Marker = require("#editorjs/marker");
const Checklist = require("#editorjs/checklist");
let content = null;
if (this.props.data !== undefined) {
content = this.props.data;
}
this.editor = new EditorJS({
holder: "editorjs",
logLevel: "ERROR",
tools: {
header: Header,
embed: {
class: Embed,
config: {
services: {
youtube: true,
coub: true,
},
},
},
list: List,
inlineCode: InlineCode,
code: Code,
table: Table,
quote: Quote,
marker: Marker,
checkList: Checklist,
delimiter: Delimiter,
},
data: content,
});
};
async onSave(e) {
let data = await this.editor.saver.save();
this.props.save(data);
}
render() {
return (
<>
<button onClick={(e) => this.onSave(e)}>Save</button>
<div id={"editorjs"} onChange={(e) => this.onChange(e)}></div>
</>
);
}
}
This implementation works in NextJS
I will update code if I find a better solution.
UPDATE 2:
The answer suggested by Rising Odegua is working.
You have to create a seperate component and then import all your tools there:
import EditorJs from "react-editor-js";
import Embed from "#editorjs/embed";
import Table from "#editorjs/table";
import List from "#editorjs/list";
import Warning from "#editorjs/warning";
import Code from "#editorjs/code";
import LinkTool from "#editorjs/link";
import Image from "#editorjs/image";
import Raw from "#editorjs/raw";
import Header from "#editorjs/header";
import Quote from "#editorjs/quote";
import Marker from "#editorjs/marker";
import CheckList from "#editorjs/checklist";
import Delimiter from "#editorjs/delimiter";
import InlineCode from "#editorjs/inline-code";
import SimpleImage from "#editorjs/simple-image";
const CustomEditor = () => {
const EDITOR_JS_TOOLS = {
embed: Embed,
table: Table,
marker: Marker,
list: List,
warning: Warning,
code: Code,
linkTool: LinkTool,
image: Image,
raw: Raw,
header: Header,
quote: Quote,
checklist: CheckList,
delimiter: Delimiter,
inlineCode: InlineCode,
simpleImage: SimpleImage
};
return (
<EditorJs tools={EDITOR_JS_TOOLS} />
);
}
export default CustomEditor;
Then in your NextJS page, use a dynamic import like this:
let CustomEditor;
if (typeof window !== "undefined") {
CustomEditor = dynamic(() => import('../src/components/CustomEditor'));
}
And you can use your component:
return (
{CustomEditor && <CustomEditor />}
)
Source : https://github.com/Jungwoo-An/react-editor-js/issues/31

Dynamically rendered Tag is always lowercase

I am trying to output some svgs and output them from a list, here is my render method:
render() {
const renderTag = () => {
const Tag = this.props.id
return(<Tag />)
}
return (
<div key={this.props.name} className="social-box">
<a className={this.props.id + "-link"}>
{renderTag()}
</a>
</div>
)
}
However, the DOM node is always lowercase i.e. <facebook> rather than <Facebook> this.props.id is correctly rendered to the console as Facebook. Can anyone tell me why react or the browser incorrectly renders as lowercase, and therefore not the component, and how to fix?
It's a technical implementation of React, all tags get lowercased on this line here, AFAIK it's not possible to render non-lowercased tags and that is by design.
Read more here.
i suggest that you would take a look at this article about dynamic components.
The most relevant example from the article:
import React, { Component } from 'react';
import FooComponent from './foo-component';
import BarComponent from './bar-component';
class MyComponent extends Component {
components = {
foo: FooComponent,
bar: BarComponent
};
render() {
const TagName = this.components[this.props.tag || 'foo'];
return <TagName />
}
}
export default MyComponent;
you most likely have a limited amount of components that could be rendered, so you might create a dictionary that contain a key (name of the component) to the component itself (as shown in the example) and just use it that way:
import Facebook from './FaceBook';
import Twitter from './Twitter';
const components = {
facebook: Facebook,
twitter: Twitter
};
render() {
return <div key={this.props.name} className="social-box">
<a className={this.props.id + "-link"}>
<components[this.props.id] />
</a>
</div>;
}
I find the answer eventually. #TomMendelson almost had the answer, but it needed fleshing out a bit more.
A function to create the component outside of the render method, suggested by #ShubhamKhatri actually did the job. Here's the final code:
import React from 'react';
import Facebook from './svg/Facebook';
import LinkedIn from './svg/LinkedIn';
import Twitter from './svg/Twitter';
import Pinterest from './svg/Pinterest';
class SocialMediaBox extends React.Component {
renderElement(item) {
const Components = {
'Facebook': Facebook,
'Twitter': Twitter,
'Pinterest': Pinterest,
'LinkedIn': LinkedIn
}
return React.createElement(Components[item], item);
}
render() {
const Element = this.renderElement(this.props.id)
return
(
<div>
{Element}
</div>
)
}
}
export default SocialMediaBox;
Thanks for the question and answers; alongside the answers given in Dynamic tag name in jsx and React they helped me to find a solution in my context (making a functional component in Gatsby with gatsby-plugin-react-svg installed):
import React from "react"
import FirstIcon from "../svgs/first-icon.inline.svg"
import SecondIcon from "../svgs/second-icon.inline.svg"
import ThirdIcon from "../svgs/third-icon.inline.svg"
const MyComponent = () => {
const sections = [
{ heading: "First Section", icon: () => <FirstIcon /> },
{ heading: "Second Section", icon: () => <SecondIcon /> },
{ heading: "Third Section", icon: () => <ThirdIcon /> },
]
return (
<>
{sections.map((item, index) => {
const Icon = item.icon
return (
<section key={index}>
<Icon />
<h2>{item.heading}</h2>
</section>
)
})}
</>
)
}
export default MyComponent
As mine is a Gatsby project I used the above mentioned plugin, but it itself process svgs with svg-react-loader so the basic principle should work in any React project using this package.

--How to make this React/Redux code DRY

I have repetitive code that I do not know how to make DRY ( Don't Repeat Yourself ).
Here are two components "talking" via dispatch() and React's auto re-render.
this.map is repeated twice.
This module will dispatch actions on a click.
import React from 'react';
import { connect } from 'react-redux';
class Icon extends React.Component {
constructor(props) {
super(props);
this.map = {
paper: 'bg_paper.jpg',
light_wood: 'bg_wood.jpg',
graph: 'bg_graph.jpg'
};
}
flip () {
this.props.dispatch({type: 'updateIcon', bg_key: $A.nextKey(this.map, this.props.state.bg_key)});
}
render () {
const style = {
// ... snip
}
return (
<img id = 'bar_icon' onClick={this.flip.bind(this)} style={style} src='_images/sv_favicon.svg'/>
)
}
}
const mapStateToProps = state => {
return {
state: state.Icon
}
}
export default connect(mapStateToProps)(Icon);
while this component will auto re-render. It all works fine. I just want to make it DRY.
import React from 'react';
import { connect } from 'react-redux';
// ... snip
class FrameBody extends React.Component {
constructor(props) {
super(props);
this.map = {
paper: 'bg_paper.jpg',
light_wood: 'bg_wood.jpg',
graph: 'bg_graph.jpg'
};
}
render () {
const style = {
backgroundImage: 'url(' + '_images/' + this.map[this.props.state.bg_key] + ')'
};
return (
<div id='contents' style={style}>
</div>
)
}
}
const mapStateToProps = state => {
return {
state: state.Icon
}
}
export default connect(mapStateToProps)(FrameBody);
What can I do so that there are not two instances of this.map?
You can extract the logic of this.map out to a class function.
getBackgroundImageKey = () => {
const backgroundMap = {
paper: 'bg_paper.jpg',
light_wood: 'bg_wood.jpg',
graph: 'bg_graph.jpg'
}
return backgroundMap[this.props.bg_key]
}
Take a step further and add another function to return the URL and add string interpolation.
getBackgroundImageURL(){
const backgroundMap = {
paper: 'bg_paper.jpg',
light_wood: 'bg_wood.jpg',
graph: 'bg_graph.jpg'
}
return `url(_images/${backgroundMap[this.props.bg_key]})`;
}
Which will let you define the style like this
const backgroundImage = this.getBackgroundImageURL()
const style = { backgroundImage };
Well since you're already using Redux and dispatching an action to flip, why don't you move that logic there?
Keep the current image in the store so you can get it in connect, make your flip action creator a thunk that holds that "map" and decides what's the next image.
Instead of DRYness, your code lacks separation of concerns. The switch/Icon UI component would be much more reusable and terse if it only called a prop whenever the user clicks "flips". Connect this onFlip to the action creator I mentioned and you have the logic in one place, and the UI to interact in another.

React/Redux/ReactDnd - Error: removeComponentAsRefFrom

I am trying to apply a basic example of react DnD to my project (as a proof of concept) and am running into some issues. I am using this example - https://github.com/gaearon/react-dnd/tree/master/examples/02%20Drag%20Around/Naive . which just let you drag around some divs inside a box.
So I have an outer component (Note: this is a few component levels up from the actual drag component) which is basically the whole page (this is just for testing a POC at first) which looks like so:
import { DropTarget, DragDropContext } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
const boxTarget = {
drop(props, monitor, component) {
const item = monitor.getItem();
const delta = monitor.getDifferenceFromInitialOffset();
const left = Math.round(item.left + delta.x);
const top = Math.round(item.top + delta.y);
component.moveBox(item.id, left, top);
}
};
// eslint-disable-next-line new-cap
#DragDropContext(HTML5Backend)
// eslint-disable-next-line new-cap
#DropTarget('CLA_DRAG', boxTarget, (connect) => ({
connectDropTarget: connect.dropTarget()
}))
export default class GenericPlatformComponent extends React.Component {
...
render() {
const { connectDropTarget } = this.props;
return connectDropTarget(
<div>
{this.getBuilders()}
</div>
);
}
}
So pretty close to the examples container, just using a string for now instead of that constant in #DropTarget . The getBuilders just renders other components inside. I Ran this component before creating the DragSource component and everything was running fine.
So, a few components down I just added the react DnD syntax to the component I want to be draggable like so :
import { DragSource } from 'react-dnd';
const boxSource = {
beginDrag(props) {
const { id, left, top } = props;
return { id, left, top };
}
};
// eslint-disable-next-line new-cap
#DragSource('CLA_DRAG', boxSource, (connect, monitor) => ({
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging()
}))
export default class SearchResult extends React.Component {
constructor(props) {
super(props);
}
render() {
const { connectDragSource } = this.props;
return connectDragSource(
<div key={this.props.key}>
<Row>
<Col xs={2} sm={2} md={1} lg={1} >
<div style={tempThumbnail}>
Picture Here
</div>
</Col>
<Col xs={10} sm={10} md={11} lg={11} >
<DescriptionBlock result={this.props.result} {...this.props} />
</Col>
</Row>
</div>
);
}
}
So with all this, I am getting this error on the console (the app is stopping rendering too).
bundle.js:29605 Uncaught (in promise) Error: removeComponentAsRefFrom(...): Only a ReactOwner can have refs. You might be removing a ref to a component that was not created inside a component's `render` method, or you have multiple copies of React loaded (details: react-refs-must-have-owner).(…)
What is strange is the dragsource isn't even called onto the dom yet, so I have no idea where to start with this error. Any input or suggestions would be greatly appreciated. Thanks!
Maybe you omitted it, but you should be importing React in both of your examples:
import React from 'react'

Categories

Resources