I'm trying to add the Carousel image slider from MaterializeCSS to a simple React component but i'm not able to initialize it! It would be very helpful to know where i should do it in my code
import React, { Component } from 'react';
import { M } from 'materialize-css/dist/js/materialize.min.js';
export default class Slider extends Component {
componentDidMount() {
var elem = document.querySelector('.carousel');
var instance = M.Carousel.init(elem, { duration: 200 });
}
render() {
return (
<div className="container center-align">
<h1 className="header pink-text">Slider</h1>
<div className="carousel">
<a className="carousel-item" href="#one!">
<img src="https://www.w3schools.com/images/picture.jpg" />
</a>
<a className="carousel-item" href="#two!">
<img src="../../public/images/lana/1.jpg" />
</a>
<a className="carousel-item" href="#three!">
<img src="../../public/images/lana/1.jpg" />
</a>
<a className="carousel-item" href="#four!">
<img src="../../public/images/lana/1.jpg" />
</a>
<a className="carousel-item" href="#five!">
<img src="../../public/images/lana/1.jpg" />
</a>
</div>
</div>
);
}
}
this gives me an Error:
Cannot read property 'Carousel' of undefined
i tried to do it with Jquery, no errors but didn't work!
I had the same issue.
Solved it by adding the materializecss npm module
npm install materialize-css
and then importing it into the component
import M from "materialize-css";
and in the componentDidUpdate method, added the init
componentDidUpdate() {
let collapsible = document.querySelectorAll(".collapsible");
M.Collapsible.init(collapsible, {});
}
Problem solved!!
Using React Hook with useEffect you can also initialize each feature on Materialize, for example:
import React, { useEffect, Fragment } from "react";
import M from "materialize-css/dist/js/materialize.min.js";
useEffect(() => {
// Init Tabs Materialize JS
let tabs = document.querySelectorAll(".tabs");
M.Tabs.init(tabs);
});
In my case, I used tabs to initialize the Materialize feature.
Looks like you're using a .min file to import M from. You should install MaterializeCSS as a node module instead. You're getting an error because M is not defined as anything. There aren't any exports from that .min file.
If you want to wait until the script has loaded it is better to do that with a callback instead of setTimeout.
function loadScript(url, callback) {
var script = document.createElement("script")
script.type = "text/javascript";
if (callback) {
script.onload = callback;
}
document.body.appendChild(script)
script.src = url;
}
loadScript(pathtoscript, function() {
alert('script ready!');
});
If you want to use min.js file in your application, try adding it in html file using tag.
Alternatively try adding node modules using npm package.
Hope this helps.
I figured out that it needs some time to render the content. When I use setTimeout() function and call that initialization JS lines it worked.
I initialize the Carousel js code from the main html page with setTimeout() function, i used a spinner to make look little better
it looks like this
<script>
setTimeout(() => {
var elem = document.querySelector('.carousel');
var instance = M.Carousel.init(elem, {});
if (document.querySelector('.photos').classList) {
document.querySelector('.photos').classList.remove("spinner")
}
}, 2000)
</script>
add window.M. ...
useEffect(() => {
var elem = document.querySelector(".carousel");
var instance = window.M.Carousel.init(elem, {
fullWidth: true,
indicators: true,
});
});
Related
When I place js script into "public" folder then import this way into script tag script.src = "/scoper.js";, js script runs fine. But when I move that js script into "src" folder then directly import it, script runs but not properly. So what is the difference when running js script from <script src="..."> and running from import { scoper } from "./scoper.js";?https://codesandbox.io/s/stylesheet-scoping-b0k9pp?file=/src/App.js:65-102
When script runs successfully, "Hello World" text should be red color.
App.js
import React, { useEffect } from "react";
import "./styles.css";
import { scoper } from "./scoper.js"; // this doesn't work
export default function App() {
useEffect(() => {
const script = document.createElement("script");
// script.src = "/scoper.js"; //this works
script.async = true;
document.body.appendChild(script);
let style = document.createElement("style");
style.setAttribute("scoped", "");
style.innerHTML =
".Puma {" +
"color: purple;" +
"font-size: 50px;" +
"text-align: left;" +
"}";
let main = document.getElementById("raptors");
main.appendChild(style);
return () => {
document.body.removeChild(script);
};
}, []);
return (
<>
<div id="lakers" className="Puma">
<div>Hello World!</div>
</div>
<div id="raptors" className="Puma">
<h1>Raptors win!</h1>
</div>
</>
);
}
With import { scoper } from "./scoper.js"; the script is imported and executed when the file App.js is executed the first time, that is the time when e.g. function App() { ... is defined, but before App() gets executed, and before any components are rendered.
useEffect(() => { ... is executed after the component is rendered. So at the time the imported script is executed, the DOM elements rendered by React are available.
Generally, you should not execute the script inside a module immediately when it is imported, because that will always be confusing (there are exceptions, of course). Instead you should export an object or function, so that the importing file can choose when to call it. In case the importing file wants to execute the imported function immediately, it still can do that.
I have scripts In my React app that are inserted dynamically later on. The scripts don't load.
In my database there is a field called content, which contains data that includes html and javascript. There are many records and each record can include multiple scripts in the content field. So it's not really an option to statically specify each of the script-urls in my React app. The field for a record could for example look like:
<p>Some text and html</p>
<div id="xxx_hype_container">
<script type="text/javascript" charset="utf-8" src="https://example.com/uploads/hype_generated_script.js?499892"></script>
</div>
<div style="display: none;" aria-hidden="true">
<div>Some text.</div>
Etc…
I call on this field in my React app using dangerouslySetInnerHTML:
render() {
return (
<div data-page="clarifies">
<div className="container">
<div dangerouslySetInnerHTML={{ __html: post.content }} />
... some other data
</div>
</div>
);
}
It correctly loads the data from the database and displays the html from that data. However, the Javascript does not get executed. I think the script doesn't work because it is dynamically inserted later on. How can I make these scripts work/run?
This post suggest a solution for dynamically inserted scripts, but I don't think I can apply this solution because in my case the script/code is inserted from a database (so how to then use nodeScriptReplace on the code...?). Any suggestions how I might make my scripts work?
Update in response to #lissettdm their answer:
constructor(props) {
this.ref = React.createRef();
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.postData !== this.props.postData) {
this.setState({
loading: false,
post: this.props.postData.data,
//etc
});
setTimeout(() => parseElements());
console.log(this.props.postData.data.content);
// returns html string like: `<div id="hype_container" style="margin: auto; etc.`
const node = document.createRange().createContextualFragment(this.props.postData.data.content);
console.log(JSON.stringify(this.ref));
// returns {"current":null}
console.log(node);
// returns [object DocumentFragment]
this.ref.current.appendChild(node);
// produces error "Cannot read properties of null"
}
}
render() {
const { history } = this.props;
/etc.
return (
{loading ? (
some code
) : (
<div data-page="clarifies">
<div className="container">
<div ref={this.ref}></div>
... some other data
</div>
</div>
);
);
}
The this.ref.current.appendChild(node); line produces the error:
TypeError: Cannot read properties of null (reading 'appendChild')
If your are sure about HTML string content is safety and contains a string with valid HTML you can use Range.createContextualFragment() (executes scripts 🚨)
function App() {
const ref = useRef();
useEffect(() => {
/* convert your HTML string into DocumentFragment*/
const node = document.createRange().createContextualFragment(HTML);
ref.current.appendChild(node);
}, []);
return (
<div>
<h1>HTML String</h1>
<div>
<div ref={ref}></div>
</div>
</div>
);
}
See how script content is executed on JavaScript console working example
If your are using class component create ref within class constructor, then update node content, I did it in componentDidMount just for testing:
class App extends React.Component {
constructor(props) {
super(props);
this.ref = React.createRef();
}
componentDidMount() {
const node = document.createRange().createContextualFragment(HTML);
this.ref.current.appendChild(node);
}
render() {
return (
<div>
<h1>HTML String</h1>
<div>
<div ref={this.ref}></div>
</div>
</div>
);
}
}
see this working example
There are various ways to do this. You may create a function that can be called on to dynamically create and inject the <script> tag into the <body> of the React application.
const addScript = () => {
const script = document.createElement("script");
script.src = '<url-of-the-script>';
script.async = true;
script.onload = function() {
// Do something
};
document.head.appendChild(script);
}
You may call this addScript function when the required component loads using the useEffect hook.
useEffect(() => {
addScript();
return () => {
// remove the script on component unmount
};
}, []);
Rendering raw HTML without React recommended method is not a good practice. React recommends method dangerouslySetInnerHTML to render raw HTML.
You would need to first fetch the dynamic data from the db using a fetch call and make use of useEffect, Inside which after the data is fetched you set it to a useState hook variable which will hold the data for this.
const [dbData,setDbData] = useState([]);
useEffect(()=>{
const dbReqFunc = async () => {
// req your dynamic data here and set the data fetched to dbData
// using setDbData(...)
};
dbReqFunc();
},[]); // Making the call on component loading (Modify accordingly based on
// needs)
After this once the data has been set you can make use of another useEffect hook which should be below the previous useEffect hook. Then you can call your function for the entire dynamic URL fetched and append it to the HTML document ( I have attached all the scripts to a div for easy cleanup).
useEffect(()=>{
if(dbData.length>0){
const elem = document.createElement('div');
dbData.map(data=> {
// based on data returned modify accordingly
const script = document.createElement("script");
script.src = '...script-url...';
script.async = true;
script.onload = function() {
// Do something
};
//...other script fields...
elem.appendChild(script);
});
// attach elem to body
document.body.appendChild(elem);
return ()=>{
// clean-up function
document.body.removeChild(elem);
};
}
},[dbData]);
This should load the script data and should load the scripts.
NOTE: Make sure you are putting the the dynamic db call for fetching the data before the 2nd useEffect. So, that it runs in order.
Is it possible to run html scripts in a specific React component only, instead of directly in index.html. The script loads a third party barcode scanner, which is only being used in one component, and therefore I want to avoid loading it for every component as this will slow the whole app down.
The npm module can be found here: https://www.npmjs.com/package/dynamsoft-javascript-barcode but there is no documentation on how to import it, only to include it like this:
<script src="https://cdn.jsdelivr.net/npm/dynamsoft-javascript-barcode#7.2.2-v2/dist/dbr.js" data-productKeys="LICENSE-KEY"></script>
<script>
let barcodeScanner = null;
Dynamsoft.BarcodeScanner.createInstance({
onFrameRead: results => {console.log(results);},
onUnduplicatedRead: (txt, result) => {alert(txt);}
}).then(scanner => {
barcodeScanner = scanner;
barcodeScanner.show();
});
</script>
Here is the React sample provided by Dynamsoft GitHub repository: https://github.com/dynamsoft-dbr/javascript-barcode/tree/master/example/web/react
You can check out Dynamsoft.js and HelloWorld.js to see how to import and use the module:
import Dynamsoft from "dynamsoft-javascript-barcode";
Dynamsoft.BarcodeReader.engineResourcePath = "https://cdn.jsdelivr.net/npm/dynamsoft-javascript-barcode#7.3.0-v1/dist/";
// Please visit https://www.dynamsoft.com/CustomerPortal/Portal/TrialLicense.aspx to get a trial license
Dynamsoft.BarcodeReader.productKeys = "PRODUCT-KEYS";
// Dynamsoft.BarcodeReader._bUseFullFeature = true; // Control of loading min wasm or full wasm.
export default Dynamsoft;
import Dynamsoft from "../Dynamsoft";
import React from 'react';
class HelloWorld extends React.Component {
...
let reader = this.reader = this.reader || await Dynamsoft.BarcodeReader.createInstance();
...
}
Note: This sample uses 7.3.0-v1. 7.2.2-v2 does not support this usage.
I haven't tried this code but I have done some code like this.You can add script tag like this:
class YourComponent extends React.Component {
loadScript() {
let script = document.createElement("script");
script.src = "https://cdn.jsdelivr.net/npm/dynamsoft-javascript-barcode#7.2.2-v2/dist/dbr.js";
script.setAttribute("data-productKeys","LICENSE-KEY");
script.type = "text/javascript";
document.head.append(script);
}
componentWillMount() {
if(!YourComponent.bScriptLoaded){
this.loadScript();
YourComponent.bScriptLoaded = true;
}
}
}
This will add script tag in head tag and.And after that you can run your code in component.
I am trying to get access to a React DOM element 'id' from an external script file. I believe my script is being imported correctly as console.log('test') from the file is working, though console.log(myDiv) returns null.
How can I achieve this in React?
// COMPONENT
import './../data/script.ts';
render() {
return (
<div id='targetDiv'>
<p>{This will be populated from my external script file...}</p>
</div>
);
}
// SCRIPT
var myDiv = document.getElementById('targetDiv');
console.log(myDiv);
To fix the issue I needed to import my external script as a function, and then call the function after the component mounted:
// COMPONENT
import { myScript } from './../data/script';
componentDidMount() {
{
myScript();
}
}
render() {
return (
<div id='targetDiv'>
{My script now renders correctly inside the div}
</div>
);
}
// SCRIPT
var myDiv = document.getElementById('targetDiv');
const d = document.createElement('p');
myDiv.appendChild(d);
React.js
I have a problem with React.js code below.
I'm trying to set the animation before page transition using "React-router.Link." and ReactCSSTransitionGroup.
version
react: '15.2.1'
react-addons-css-transition-group: '15.2'
react-router: '2.6.0'
I want get the lifecycle event, so I can use JS not CSS.
If you know the right way to do, please let me know.
Thank you.
ex.) componentWillLeave etc...
P.S
I tried this code but componentWillLeave is not fire.
var React = require("react");
var ReactRouter = require("react-router");
var CSSTransitionGroup = require('react-addons-css-transition-group');
var Link = ReactRouter.Link;
var Test = React.createClass({
componentWillLeave: function(callback) {
console.log("component will leave");
$(this.getDOMNode()).hide(duration, callback);
},
render: function() {
return (
<div id="index">
<CSSTransitionGroup transitionName="example" transitionAppear={true} transitionLeave={true} transitionAppearTimeout={3000} transitionLeaveTimeout={3000}>
<Link to="/" key="toIndex">Index</Link>
<Link to="contact" key="toContact">Contact</Link>
</CSSTransitionGroup>
</div>
)
}
});
I fixed this issue by myself.
I didn't know ReactTransitionGroup.
Thanks, Evenryone.