Marketo form appearing twice in gatsby - javascript

I am having an issue while implementing marketo form with gatsby at times. I have created a custom component for marketo forms which works smoothly when an input form id is given.
I am using Drupal 8 as backend for gatsby front end, so all content for the site comes dynamically from drupal.
This is the code for custom component :
import React, { useEffect, useState } from 'react'
import { Link } from "gatsby"
const MarketoForm = ({ fid }) => {
let formId = `mktoForm_${fid}`
const [formSuccess, setFormSuccess] = useState(false)
let message = ''
switch (fid) {
case 1537: // get in touch form
message = <>
<h1>Thanks for the message!</h1>
</>
break;
default:
message = <>
<h1>Thanks for the message!</h1>
</>
break;
}
useEffect(() => {
const script = document.createElement('script');
document.body.appendChild(script);
// Depending on your lint, you may need this
// eslint-disable-next-line no-undef
MktoForms2.loadForm("//app-lon03.marketo.com", "416-MPU-256", fid, function (form) {
// Add an onSuccess handler
form.onSuccess(function (values, followUpUrl) {
// Get the form's jQuery element and hide it
form.getFormElem().hide();
// Return false to prevent the submission handler from taking the lead to the follow up url
setFormSuccess(true);
return false;
});
});
}, [])
return (
<>
{formSuccess ?
<div>
{message}
</div> : <form id={formId}></form>}
</>
)
}
export default MarketoForm
and if I call it like
<MarketoForm fid={146} />
It works perfectly without any issues.
I am trying to load html to a particular div, this html contains script as well.I have written code so that this code will be executed as well.
class article extends Component {
state = {
externalScript: ''
}
componentDidMount() {
let htmlContent = this.props.data && this.props.data.article &&
this.props.data.article.body.processed
if (htmlContent.length > 0) {
if(/<script>[\s\S]*<\/script>/g.exec(htmlContent)){
let extractedScript = /<script>[\s\S]*<\/script>/g.exec(htmlContent)[0];
this.setState({ externalScript: extractedScript })
}
}
}
componentDidUpdate(){
let scriptToRun = this.state.externalScript
if (scriptToRun !== undefined && scriptToRun !== '') {
let scriptLines = scriptToRun.split("\n")
scriptLines.pop()
scriptLines.shift()
let cleanScript = scriptLines.join("\n")
window.eval(cleanScript)
}
}
render(){
// fetches data here ....
<>
<MarketoForm fid={142} />
<MarketoForm fid={156} />
<div dangerouslySetInnerHTML={{ __html: article.body.processed }}>
</div>
</>
}
}
So in this article component other marketo form already exists which works fine. At times I can see the same form appearing more than once inside the div (content is from backend).
Upon inspecting the form i can see its just one form not two.
Have anyone faced this issue before? Please suggest some resolutions for this.
Thanks

Related

Implement Edit feature in a Note Application using react

I am trying to create a notes application wherein each note object contains a title and content. The user can add, deleted and update a note.
What I was able to achieve so far:
I am able to create a new note , push it into an array and also delete a note from the array . I am finding it a bit hard to edit an existing note.
This is how I want to implement the edit feature:
When the user clicks on the note, the data has to automatically fill into the input box, and the user can modify the data which is then saved into an object and pushed inside an array and then displayed onto the respective note.
When the user clicks on the Edit button, the note id is sent to the App component, the note is searched within the notes array and an object returned to the Create Area component. This object is then displayed on the input field. I'm using UseEffect() hook to display the object data on the input box, but I'm not able to edit the contents on the input box. Here's my code below:
App.jsx:
If the user clicked the edit button, it sets the IsDone state to true in the Edit function. The edit function gets an object from the Notes component
import Header from "./Header";
import CreateArea from "./CreateArea";
import Note from "./Note";
import Footer from "./Footer";
import { useState } from "react";
function App() {
const [noteArray, setArray] = useState([]);
const [isDone,setDone] = useState(false);
const [editNote,setEditNote] = useState({
title:"",
content:""});
function AddOnClick(note) {
setArray((prevNote) => {
return [...prevNote, note];
});
}
function DeleteOnClick(id) {
setArray((prevNote) => {
return prevNote.filter((note, index) => {
return index !== id;
});
});
}
function EditNote(obj)
{
setDone(true);
setEditNote(prevState=>{
return { ...prevState,...obj}});
}
return (
<div>
<Header />
<CreateArea AddOnClick={AddOnClick} noteEdit = {editNote} editFunction = {EditNote}btnClicked = {isDone}/>
{noteArray.map((note, index) => (
<Note
key={index}
id={index}
title={note.title}
content={note.content}
deleteNote={DeleteOnClick}
EditNote = {EditNote}
/>
))}
<Footer />
</div>
);
}
export default App;
Notes.jsx: The id of the note is also included in the object that's passed to App component through the EditNote() function
function Note(props) {
const obj = {title : props.title,
content: props.content,
id:props.id}
return (
<div className="note">
<h1>{props.title}</h1>
<p>{props.content}</p>
<button
onClick={() => {
props.deleteNote(props.id);
}}
>
DELETE
</button>
<button onClick={()=>{props.EditNote(obj)}}>EDIT</button>
</div>
);
}
export default Note;
CreateArea: If the buttonClicked value is true, I'm calling the handleEdit() that takes the object sent from the EditNote() in App.jsx to saves it to to note object using useState() which automatically updates the input and text area field using event.target.value with the help of useEffect().
import { useState } from "react";
function CreateArea(props) {
const [note, setNote] = useState({
title: "",
content: ""
});
function handleChange(event) {
console.log(event.target);
const { name, value } = event.target;
setNote((prevNote) => {
return { ...prevNote, [name]: value };
});
}
function addNote(event) {
setNote({ title: "", content: "" });
props.AddOnClick(note,note.id);
event.preventDefault();
}
function handleEdit()
{
setNote(prevValue=>{
return {...prevValue,...props.noteEdit}
})
}
useEffect (()=>{
if(props.btnClicked){handleEdit();
}
});
return (
<div>
<form>
<input name="title" id="title" value={note.title}onChange={handleChange}placeholder="Title"/>
<textarea name="content" id="content" value={note.content}onChange={handleChange} placeholder="Take a note..." rows="3"/>
<button onClick={addNote}>Add</button>
</form>
</div>
);
}
export default CreateArea;
The code runs well but now I can't add any more text on the input box,it just blocks me from doing it.I tried calling HandleChange() inside UseEffect(), that throws an error saying: Cannot read properties of target:undefined at HandleChange() I really need help how to implement edit.
I tried directly populating the input box and the text area field using document.getElementById.value = myValue even that does not seem to work

Issues rendering the right dashboard UI user based on roles

I am trying to render UI in my project based on selected roles (brands, agency, influencer) on click. However, the logic that I am putting together is not loading the right UI and I don't quiet understand why not.
I have tried moving the role and setRole to the top component and passed the props down to the child components that read role and updated it via setRole so that I can have the state to be available in two places.
I also set a logic that should display components based on if the role equals the value of the buttons.
What happened was the components weren't loading upon clicking the function that handles click. However, logging out to the console if the role equals the value of the clicked button returns true, the right string that the logic was based on.
What I am expecting to happen is to load the component e.g: "Brands" when users click and select "brands" which is the value of the clicked button. Vise versa for the other components.
My code is as follows:
import { useState } from 'react';
import { useSession } from 'next-auth/react';
import Brands from './Brands';
import Agency from './Agency';
import CreatorsDash from './CreatorsDashboard';
export default function FirstPageModal({ role: userRole }) {
const [role, setRole] = useState(userRole);
const { data: session } = useSession();
const handleClick = (e) => {
e.preventDefault();
let buttonValue = e.target.value;
const clickedRole = role?.map((user) => {
let { role } = user;
if (buttonValue) {
userRole = { role: buttonValue };
}
return { userRole };
});
console.log(clickedRole); //Returns an array
clickedRole.map((item) => {
const { role } = item.userRole;
console.log(role); //Returns string ("agency" / "brands" / "Influencer")
if (session && role === 'brands') {
console.log(role); //This logs "brands" as expected but doesn't return the component
// return <Brands session={session} role={role} />;
} else if (session && role === 'agency') {
return <Agency session={session} role={role} />;
} else if (session && role === 'Influencer') {
return <CreatorsDash session={session} role={role} />;
} else {
console.log('Select a role');
}
});
};
return (
<>
<div className="">
<button type="button" className="" onClick={handleClick} value="agency">
As an Agency
</button>
<button type="button" className="" onClick={handleClick} value="brands">
As a Brand
</button>
<button
type="button"
className=""
onClick={handleClick}
value="Influencer"
>
As an Influencer
</button>
</div>
</>
);
}
Returning a component from an onClick handler doesn't automatically render the component. One thing you could do is to keep track of the role in the state and then put the <Brands /> <Agency/> and <CreatorsDash /> components in the render function and dynamically show/hide them like {role === "brands" && <Brands />. This can also be done with css, although the benefits of this are not so clear,.
Side note, it is very helpful to post a codepen with your code, especially as your code gets more complicated

why is my if else statement returns true two times?

so I am new to React and I am trying to learn the basics. I got stuck at a point, where I try to show/hide an element on a page. The problem is that when I click Show details, it works, but Hide details must be clicked 2 times in order to do what its supposed to do.
Can you help me understand this?
import React, { useState } from 'react'
const Playground2 = () => {
let visible = false;
const [details, showDetails] = useState();
const [buttonText, changeButtonText] = useState("Show details");
const toggleVisibility = () => {
if (visible) {
showDetails("");
visible = false;
console.log(visible);
changeButtonText("Show details")
} else {
showDetails("Here are some details");
visible = true;
console.log(visible);
changeButtonText("Hide details");
}
}
return (
<>
<section>
<div className="container">
<h1>Visibility Toggle</h1>
<button onClick={toggleVisibility}>{buttonText}</button>
<p>{details}</p>
</div>
</section>
</>
)
}
export default Playground2
You should use the state in your condition. If you declare a variable like your visible one, this will be assigned on every render (every time you set the state with changeButtonText or showDetails. So every time will be set to false. You can simplify your component by doing:
import React, { useState } from 'react'
const Playground2 = () => {
const [visible, setVisible] = useState();
const toggleVisibility = () => {
setVisible(prevState => !prevState)
}
const buttonText = visible ? 'Hide details' : 'Show details'
const details = 'Here are some details'
return (
<>
<section>
<div className="container">
<h1>Visibility Toggle</h1>
<button onClick={toggleVisibility}>{buttonText}</button>
{visible && <p>{details}</p>}
</div>
</section>
</>
)
}
export default Playground2
Well it'd solve your problem if you turn visibility to state as well.
What I think happening is that when you click on button, the visibility variable is turned to false but component isn't refreshed. In order for component to get refreshed, there must be some change in state.
Maybe try that. That should do the trick.
Tip: Variables like loading, visibility, modal closed/open should be state variables.
Move let visible = false out of the component body and this will work as expected, since you are putting visible inside Component, every time the component updates false will be stored in visible.
let visible = false
const Playground 2 = () => {}

ReactJS what is the proper way to wait for prop to load to avoid crashing my page?

all,
I am building a local website for myself for stocks. Currently I have a store that communicates with my tomcat instance to get stock market data, this works flawlessly.
on my frontend I am attempting to display my data but sometimes it works, sometimes it does not work and I get an "this child prop does not exist" so this is what I implemented:
try{
cellRend = this.cellRenderer;
columnLen = this.props.selectedStock.Revenue.length;
this.state.isLoading = false
}catch(error){
cellRend = this.cellRendererEmpty;
columnLen = 10;
}
if (this.state.isLoading === true){
return <div>Loading!</div>
}
where cellRenderer is my table, cellRendererEmpty is an empty table.
this kind of works and some times it will just display Loading! forever. so my question is what is the correct way to wait for a prop?
here is my full code:
const dispatchToProps = dispatch => {
return{
getSelStock: (stockId) => dispatch(stockActions.getSelStock(stockId))
};
}
class stockPage extends React.Component{
constructor(props) {
super(props);
this.state = {
columnLen:10,
data:null,
isLoading:true
}
console.log(this.props.isLoading)
this.cellRenderer = this.cellRenderer.bind(this);
this.render = this.render.bind(this);
}
cellRenderer({ columnIndex, key, rowIndex, style }) {
return (
<div className={"app"} key={key} style={style}>
<span></span>
{rowIndex === 0 ? (`${this.props.selectedStock.Revenue[columnIndex].date}`) : (
<span>{`${this.props.selectedStock.Revenue[columnIndex].value}`}</span>
)}
</div>
);
}
cellRendererEmpty({ columnIndex, key, rowIndex, style }) {
return (
<div className={"app"} key={key} style={style}>
{rowIndex === 0 ? (`${columnIndex}`) : (
<span>{`${columnIndex}`}</span>
)}
</div>
);
}
render() {
var cellRend, columnLen
console.log("Hey")
this.props.getSelStock(this.props.match.params.stockId);
try{
cellRend = this.cellRenderer;
columnLen = this.props.selectedStock.Revenue.length;
this.state.isLoading = false
}catch(error){
cellRend = this.cellRendererEmpty;
columnLen = 10;
}
if (this.state.isLoading === true){
return <div>Loading!</div>
}
return(
<div>
<h1>{this.props.match.params.stockId}</h1>
<AutoSizer disableHeight>
{({ width }) => (
<MultiGrid
cellRenderer={cellRend}
columnWidth={125}
columnCount={this.state.columnLen}
enableFixedColumnScroll ={1}
enableFixedRowScroll ={1}
fixedColumnCount
fixedRowCount
height={300}
rowHeight={70}
rowCount={2}
style={STYLE}
styleBottomLeftGrid={STYLE_BOTTOM_LEFT_GRID}
styleTopLeftGrid={STYLE_TOP_LEFT_GRID}
styleTopRightGrid={STYLE_TOP_RIGHT_GRID}
width={width}
hideTopRightGridScrollbar
hideBottomLeftGridScrollbar
hideBottomRightGridScrollbar
/>
)}
</AutoSizer>
</div>
)
}
}
export default connect(mapStateToProps, dispatchToProps)(stockPage);
From your title, I assume that when your page loads, you are fetching data then you use that data in the page. However, during initial load and when your fetching is still in process, your data is still null and your app will crash because the code is expecting data to have a value which it needs to use...
What you can do is while the data is fetching, then do not display the rest of the page yet (ie. you can just display a giant spinner gif), then once the fetching is complete then update isLoading state... Or you can set an initial value for the data so the page won't crash on load...
EDIT:
so using react lifecycle fixed your problem as per your comment... anyways just wanna add that you may want to use async/await instead of setTimeout like you did in your comment.. This is what the code might look like for async/await in lifecycles...
componentDidMount() {
const fetchData = async () {
await this.props.getSelStock() // your dispatch
// the state you want to update once the dispatch is done
this.setState({isLoading: false})
}
fetchData();
}

Check changes before routing in React / Next js

I am having a Next JS app where there are very simple two pages.
-> Home page
import Header from "../components/header";
const handleForm = () => {
console.log("trigger");
};
export default () => (
<>
<Header />
<h1>Home</h1>
<form onSubmit={handleForm}>
<input type="text" placeholder="Username" />
<input type="password" placeholder="Password" />
<button type="submit"> Login </button>
</form>
</>
);
-> About page
import Header from "../components/header";
export default () => (
<>
<Header />
<h1>About us</h1>
</>
);
Requirement:
-> Home page has a login form
-> If user started typing in any of the fields then without submitting the form, if he tries to move to About us page then a warning needs to be displayed something similar like beforeunload_event.
I am not sure how we can handle it in react as I am new to it.. Kindly please help me to handle a alert if user trying to navigate to other url while editing the form fields..
From my understanding, you can achieve your goal by listen the event routeChangeStart as then throws exception in case of rejecting to move the target url.
I forked above codesandbox and created a simple demo based on your idea which doesn't allow to switch page in case of username having value (form is dirty).
Here is the general idea:
import router from "next/router";
export default () => {
// Assume this value holds the status of your form
const [dirty, setDirty] = React.useState();
// We need to ref to it then we can access to it properly in callback properly
const ref = React.useRef(dirty);
ref.current = dirty;
React.useEffect(() => {
// We listen to this event to determine whether to redirect or not
router.events.on("routeChangeStart", handleRouteChange);
return () => {
router.events.off("routeChangeStart", handleRouteChange);
};
}, []);
const handleRouteChange = (url) => {
console.log("App is changing to: ", url, ref.current);
// In this case we don't allow to go target path like this
// we can show modal to tell user here as well
if (ref.current) {
throw Error("stop redirect since form is dirty");
}
};
return (
// ...
)
}
The link codesandbox is here https://codesandbox.io/s/react-spring-nextjs-routes-forked-sq7uj

Categories

Resources