I'm using ReactJS with Routing, ES6, Babel and ESLint.
On click I want to add a CSS class using the classnames library, but I can't even use the document.querySelector. My whole application crashes and prints the error: ReferenceError: document is not defined
import React from 'react';
import { connect } from 'react-redux';
export default class Nav extends React.Component {
constructor(props) {
super(props);
const sideNavToggleButton = document.querySelector('.js-toggle-menu');
sideNavToggleButton.addEventListener('click', () => {
console.info('clicked');
});
}
render() {
return (
<header>
<button role="tab" className="header__menu js-toggle-menu">Toggle nav menu</button>
<h1 className="header__title">App Shell</h1>
</header>
);
}
}
After some research I found out it might be my ESLint settings missing some environments, but after adding browser the application is still breaking.
module.exports = {
'parser': 'babel-eslint',
'plugins': [
'react'
],
'rules': {
'indent': [2, 'tab'],
'max-len': 0,
'no-console': [2, { allow: ['info', 'error']}],
'no-param-reassign': 0,
'react/jsx-indent': [2, 'tab'],
'react/jsx-indent-props': [2, 'tab'],
'no-new': 0
},
'env': {
'browser': true,
'node': true
}
};
Any idea?
You have to move this part of your code to the method componentDidMount()
const sideNavToggleButton = document.querySelector('.js-toggle-menu');
sideNavToggleButton.addEventListener('click', () => {
console.info('clicked');
});
Because componentDidMount() is the method that is called after the first rendering here. After rendering you can do any dom manipulation.
Related
I'm trying to use react-image-annotate but it's giving me this issue when I first try to set it up.
And here's how I'm using it:
import React from 'react'
import ReactImageAnnotate from 'react-image-annotate'
function ImageAnnotator() {
return (
<ReactImageAnnotate
selectedImage="https://images.unsplash.com/photo-1561518776-e76a5e48f731?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=750&q=80"
// taskDescription="# Draw region around each face\n\nInclude chin and hair."
// images={[
// { src: 'https://example.com/image1.png', name: 'Image 1' },
// ]}
// regionClsList={['Man Face', 'Woman Face']}
/>
)
}
export default ImageAnnotator
I'm using Next.js if that matters
UPDATE 1
I tried using this babel plugin as suggested by Alejandro Vales. It gives the same error as before. Here's the babel key in my package.json:
"babel": {
"presets": [
"next/babel"
],
"plugins": [
[
"#babel/plugin-proposal-decorators",
{
"legacy": true
}
],
[
"#babel/plugin-transform-modules-commonjs",
{
"allowTopLevelThis": true
}
]
]
}
I would say that the issue relies in the library itself by what they replied in here (similar bug) https://github.com/UniversalDataTool/react-image-annotate/issues/90#issuecomment-683221311
Indeed one way to fix it I would say is adding babel to the project so you can transform the imports in your project to require automatically without having to change the code on your whole project.
This is the babel package you are looking for https://babeljs.io/docs/en/babel-plugin-transform-modules-commonjs
Another reason for this could be an outdated version of your package, as some people report to have this fixed after using a newer version of Create React App (https://github.com/UniversalDataTool/react-image-annotate/issues/37#issuecomment-607372287)
Another fix you could do (a little crazier depending on your resources) is forking the library, creating a CJS version of the lib, and then pushing that to the library, so you and anybody else can use that in the future.
I got a tricky solution!
Problem is that react-image-annotate can only be imported in client-side(SSR got error for import keyword)
So, let react-image-annotate in Nextjs be imported only in client side
(https://nextjs.org/docs/advanced-features/dynamic-import#with-no-ssr)
in Next Page that needs this component, You can make component like this
import dynamic from "next/dynamic";
const DynamicComponentWithNoSSR = dynamic(() => import("src/components/Upload/Annotation"), { ssr: false });
import { NextPage } from "next";
const Page: NextPage = () => {
return (
<>
<DynamicComponentWithNoSSR />
</>
);
};
export default Page;
Make component like this
//#ts-ignore
import ReactImageAnnotate from "react-image-annotate";
import React from "react";
const Annotation = () => {
return (
<ReactImageAnnotate
labelImages
regionClsList={["Alpha", "Beta", "Charlie", "Delta"]}
regionTagList={["tag1", "tag2", "tag3"]}
images={[
{
src: "https://placekitten.com/408/287",
name: "Image 1",
regions: [],
},
]}
/>
);
};
export default Annotation;
I am using react-codemirror and want to highlight the text 'Hello' in the Codemirror but the match-highlighter addon is not highlighting the same. Below is the code for the same.
import React, { Component } from 'react';
import { render } from 'react-dom';
import CodeMirror from 'react-codemirror';
import 'codemirror/lib/codemirror.css';
import 'codemirror/addon/search/match-highlighter';
import 'codemirror/mode/javascript/javascript';
class App extends Component {
constructor() {
super();
this.state = {
name: 'CodeMirror',
code: '//Test Codemirror'
};
}
updateCode(newCode) {
this.setState({
code: newCode,
});
}
render() {
let options = {
lineNumbers: true,
mode: 'javascript',
highlightSelectionMatches: {
minChars: 2,
showToken: /Hello/,
style:'matchhighlight'
},
styleActiveLine: true,
styleActiveSelected: true,
};
return (
<div>
<CodeMirror value={this.state.code} onChange={this.updateCode.bind(this)} options={options}/>
</div>
);
}
}
render(<App />, document.getElementById('root'));
Current output is in the screenshot below and the word is not highlighted.
I found a solution for this issue. Inorder to enable the highlighting one need to add a css corresponding to the style property. I added the below code in css file and it started working
.cm-matchhighlight {
background: red !important
}
Now it highlights the token properly
Is there a React example using grid.js? having errors
import React from 'react';
import { Grid } from 'gridjs';
export const TableNew = () => {
const grid = new Grid({
columns: ['Weekly', 'Fortnightly', 'Monthly', 'Annually'],
data: () => [
['Pay', 0, 0, 0],
['Taxable Income', 0, 0, 0],
['Taxes', 0, 0, 0],
],
});
console.log('grid', grid);
return <div>{grid}</div>;
};
ERROR:
Error: Objects are not valid as a React child (found: object with keys {_config}). If you meant to render a collection of children, use an array instead.
in div (at TableNew.jsx:15)
There are no docs on this, but the way it works is that it needs a ref in order to render.
import React, { useEffect, useRef } from "react";
import { Grid } from "gridjs";
const TableNew = () => {
const wrapperRef = useRef(null);
useEffect(() => {
new Grid({
columns: ["Weekly", "Fortnightly", "Monthly", "Annually"],
data: () => [
["Pay", 0, 0, 0],
["Taxable Income", 0, 0, 0],
["Taxes", 0, 0, 0]
]
}).render(wrapperRef.current);
});
return <div ref={wrapperRef} />;
};
export default TableNew;
I created a sandbox with a working example.
https://codesandbox.io/embed/vigorous-hopper-n4xig?fontsize=14&hidenavigation=1&theme=dark
There is an open issue to add a React Wrapper https://github.com/grid-js/gridjs/issues/26 (I can guess, since the issue intention is not clearly described)
Here is an example with custom pagination, search, sorting and dynamic data from an api.
const ExampleComponent = () => {
const grid = new Grid({
search: {
enabled: true,
placeholder: 'Search...'
},
sort: true,
pagination: {
enabled: true,
limit: 5,
summary: false
},
columns: [
'Id',
'Tag',
'Name'
],
data: () => {
return api.movies.list({ locationId }).then(({ data }) => {
return data.results.map(movie => [movie.id, movie.tag, movie.name])
})
}
})
useEffect(() => {
grid.render(document.getElementById('wrapper'))
}, [locationId])
return (
<div id='wrapper' />
)
}
I just published the first version of the React component for Grid.js:
Install
npm install --save gridjs-react
Also, make sure you have Grid.js installed already as it's a peer dependency of gridjs-react:
npm install --save gridjs
Usage
<Grid
data={[
['John', 'john#example.com'],
['Mike', 'mike#gmail.com']
]}
columns={['Name', 'Email']}
search={true}
pagination={{
enabled: true,
limit: 1,
}}
/>
Links
Example: https://gridjs.io/docs/integrations/react
Repo: https://github.com/grid-js/gridjs-react
Edit on CodeSandbox: https://codesandbox.io/s/gridjs-react-ddy69
I am also using Grid.js in a react context. I found I had to install both Grid.js and the React integration:
npm install gridjs
npm install gridjs-react
and then change the import to this:
import {Grid} from "gridjs-react";
I am sure there is another way without the react wrapper but this is what did the trick for me.
I've been searching for a proper guidance for integrating lightgallery.js library into my application, but after several hours I did not find any solutions. Since I'm using React, I don't want to mix it with JQuery.
I've stumbled across many similar questions like this one, but since all of them are using JQuery, I can't use their solutions.
Also, I've found react-lightgallery package (React wrapper for lightgallery.js), but it does not include video support yet.
In the lightgallery.js documentation, there is the installation guidance. After completing all of the steps, importing lightgallery.js and trying to print it (as suggested here by the library owner), empty object is being shown.
What would be the best solution for this? Are there any good alternatives?
Thanks!
I have handled it this way. May be it's not complete and the best practice, but it gives you a general view to how to handle it
import React, { PureComponent } from "react";
import Gallery from "lightgallery.js";
import "lightgallery.js/dist/css/lightgallery.min.css";
class _Gallery extends PureComponent {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
let self = this;
this.gallery = document.getElementById("lightgallery");
lightGallery(this.gallery, {
dynamic: true,
dynamicEl: [
{
src:
"1.jpg",
thumb: "1.jpg",
subHtml:
"<h4>Fading Light</h4><p>Classic view</p>"
},
{
src:
"2.jpg",
thumb: "2.jpg",
subHtml:
"<h4>Bowness Bay</h4><p>A beautiful Sunrise</p>"
},
{
src:
"3.jpg",
thumb: "3.jpg",
subHtml: "<h4>Coniston Calmness</h4><p>Beautiful morning</p>"
}
]
});
this.gallery.addEventListener("onCloseAfter", function(event) {
window.lgData[self.gallery.getAttribute("lg-uid")].destroy(true);
self.props.onCloseGallery();
});
}
render() {
return <div id="lightgallery" />;
}
}
export default _Gallery;
Here is a working example with cloudinary at Cloudinary LightGallery
The code for the Cloundinary LightGallery React Component using Styled Components and styled css grid is below.
The Code for the upload component is in my GitHub Repo at.
UploadWidget
/* eslint-disable indent */
import React, { Component, Fragment } from 'react'
import { LightgalleryProvider, LightgalleryItem } from 'react-lightgallery'
import axios from 'axios'
import styled from 'styled-components'
import { CloudinaryContext, Transformation, Image } from 'cloudinary-react'
import { Grid, Cell } from 'styled-css-grid'
import { media } from '../../utils/mediaQuery'
import 'lightgallery.js/dist/css/lightgallery.css'
import 'lg-autoplay.js'
const SectionTitle = styled.h3`
font-size: 1em;
margin: 0.67em 0;
${media.xs`
font-size: .85em;
`}
`
class Gallery extends Component {
constructor (props) {
super(props)
this.link = React.createRef()
this.state = {
gallery: [],
isOpen: false,
link: this.href,
}
}
componentDidMount () {
// Request for images tagged cats
axios.get('https://res.cloudinary.com/mansbooks/image/list/v1557911334/cats.json')
.then(res => {
console.log(res.data.resources)
this.setState({ gallery: res.data.resources })
})
}
onLink (event) {
this.setState({ link: this.href =
`https://res.cloudinary.com/mansbooks/image/upload/${data.public_id}.jpg` })
}
uploadWidget () {
let _this = this
cloudinary.openUploadWidget({ cloud_name: 'mansbooks', upload_preset: 'photos-
preset', tags: ['cats'], sources: ['local', 'url', 'camera', 'image_search',
'facebook', 'dropbox', 'instagram'], dropboxAppKey: 'Your API Key', googleApiKey:
'Your API Key' },
function (error, result) {
// Update gallery state with newly uploaded image
_this.setState({ gallery: _this.state.gallery.concat(result) })
})
}
render () {
return (
<div>
<Fragment>
<SectionTitle>Gallery by Cloudinary</SectionTitle>
<div>
<CloudinaryContext cloudName='mansbooks'>
<Grid columns='repeat(auto-fit,minmax(260px,1fr))' id='hash'>
<LightgalleryProvider>
{
this.state.gallery.map(data => {
return (
<Cell key={data.public_id}>
<LightgalleryItem group='group1' src={`https://res.cloudinary.com/mansbooks/image/upload/${data.public_id}.jpg`} data-sub-html={'data.public_id'}>
<Image publicId={data.public_id} onClick={() => this.setState({ isOpen: true })}>
<Transformation
crop='scale'
width='250'
height='170'
radius='6'
dpr='auto'
fetchFormat='auto'
responsive_placeholder='blank'
/>
</Image>
</LightgalleryItem>
</Cell>
)
})
}
</LightgalleryProvider>
</Grid>
</CloudinaryContext>
</div>
</Fragment>
</div>
)
}
}
export default Gallery
I'm trying to get a value in an object of javascript but it fails somehow. I managed to get an intended data from mongoDB by findOne method. Here is my code and console log.
const title = Questions.findOne({_id: props.match.params.id});
console.log(title);
Then console says:
Object {_id: "bpMgRnZxh5L4rQjP9", text: "Do you like apple?"}
What I wanna get is only the text in the object. I have already tried these.
console.log(title.text);
console.log(title[text]);
console.log(title["text"]);
console.log(title[0].text);
But I couldn't access to it... The error message is below.
Uncaught TypeError: Cannot read property 'text' of undefined
It sounds super easy but I couldn't solve by my self. Could anyone help me out?
Additional Context
I'm using Meteor and React. I would like to pass the text inside of the object from the container to the class. I would like to render the text in render(). But it doesn't receive any data from the container... The console.log in the container works well and shows the object.
import React, { Component } from 'react';
import { createContainer } from 'meteor/react-meteor-data';
import { Questions } from '../../api/questions.js';
import PropTypes from 'prop-types';
import { Answers } from '../../api/answers.js';
import ReactDOM from 'react-dom';
import { Chart } from 'react-google-charts';
class MapClass extends React.Component{
handleAlternate(event){
event.preventDefault();
const country = ReactDOM.findDOMNode(this.refs.textInput).value.trim();
Answers.insert({
country,
yes: false,
question_id:this.props.match._id,
createdAt: new Date(), // current time
});
ReactDOM.findDOMNode(this.refs.textInput).value = '';
}
handleSubmit(event) {
event.preventDefault();
const country = ReactDOM.findDOMNode(this.refs.textInput).value.trim();
Answers.insert({
country,
yes: true,
question_id: this.props.match.params.id,
createdAt: new Date(), // current time
});
ReactDOM.findDOMNode(this.refs.textInput).value = '';
}
constructor(props) {
super(props);
this.state = {
options: {
title: 'Age vs. Weight comparison',
},
data: [
['Country', 'Popularity'],
['South America', 12],
['Canada', 5.5],
['France', 14],
['Russia', 5],
['Australia', 3.5],
],
};
this.state.data.push(['China', 40]);
}
render() {
return (
<div>
<h1>{this.props.title.text}</h1>
<Chart
chartType="GeoChart"
data={this.state.data}
options={this.state.options}
graph_id="ScatterChart"
width="900px"
height="400px"
legend_toggle
/>
<form className="new-task" onSubmit={this.handleSubmit.bind(this)} >
<input
type="text"
ref="textInput"
placeholder="Type to add new tasks"
/>
<button type="submit">Yes</button>
<button onClick={this.handleAlternate.bind(this)}>No</button>
</form>
</div>
);
}
}
export default MapContainer = createContainer(props => {
console.log(Questions.findOne({_id: props.match.params.id}));
return {
title: Questions.findOne({_id: props.match.params.id})
};
}, MapClass);
The problem here is that at the moment when the container is mounted, the data is not yet available. Since I do not see any subscriptions in your container I assume that you handle that elsewhere and thus there is no way of knowing when the data is ready. You have 2 options.
1) move the subscription into the container and use the subscription handle ready() function to assess if the data is ready. Show a spinner or something while it is not. Read this.
2) use lodash/get function (docs) to handle empty props. You would need to
npm install --save lodash
and then
import get from 'lodash/get';
and then in your class render method:
render() {
const text = get(this.props, 'title.text', 'you-can-even-define-a-default-value-here');
// then use `text` in your h1.
return (...);
}
Does this work for you?