Access Gatsby Component from a function - reactjs

I am trying to access a Gatsby component (Anime) from outside of it.
Can not figure out what instance name this would have or how to name it.
Here is my code:
import React from 'react'
import PropTypes from 'prop-types'
import PreviewCompatibleImage from '../components/PreviewCompatibleImage'
import Anime from 'react-anime';
import VisibilitySensor from 'react-visibility-sensor';
function onChange (isVisible) {
console.log('Element is now %s', isVisible ? 'visible' : 'hidden')
}
const FeatureGrid = ({ gridItems }) => (
<div className="columns is-multiline">
<VisibilitySensor onChange={onChange}>
<Anime delay={(e, i) => i * 100}
scale={[.1, .9]}
autoplay={false}>
{gridItems.map(item => (
<div key={item.text} className="column is-3">
<section className="section">
<div className="has-text-centered">
<div
style={{
width: '160px',
display: 'inline-block',
}}
>
<PreviewCompatibleImage imageInfo={item} />
</div>
</div>
<p>{item.text}</p>
</section>
</div>
))}
</Anime>
</VisibilitySensor>
</div>
)
FeatureGrid.propTypes = {
gridItems: PropTypes.arrayOf(
PropTypes.shape({
image: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
text: PropTypes.string,
})
),
}
export default FeatureGrid
I want to get the animation to trigger from the onChange function.
How do I get the name or set the name of the Anime component so I can access it from the function?
Or is there another way I should address this?
Using a Gatsby starter netlify CMS as the base, so extending on their code, but seems that const is not the route I should take.
I want the animation to trigger when it becomes visible.
Any suggestions?

According to the docs react-visibility-sensor :
You can pass a child function, which can be convenient if you don't need to store the visibility anywhere
so maybe instead of using the onchange function you can just pass the isVisible parameter, something like:
<VisibilitySensor>
{({isVisible}) =>
<Anime delay={(e, i) => i * 100}
// the rest of your codes here ...
</Anime>
}
</VisibilitySensor>
Otherwise you can convert this function to a react component and set states, etc..

Related

Unable to load images dynamically in react app

I have this component :-
const CollectionItem = ({ item }) => (
<div className='collection-item'>
<div
className='image'
style={{
backgroundImage: `url(${item.imageUrl})`
}} />
</div>
</div>
)
my images urls are like ../../assets/images/imageName.jpg
i have tried loading a an image in the css file associated with this component with this exact url. It is working with css, but not in jsx. Please help.
Generally, how you load images in react is to import the image from the specified folder (webpack converts it into the correct source behind the scenes), and then to use that imported variable as the src of the image.
import imgSource from "../../assets/images/imageName.jpg";
const CollectionItem = ({ item }) => (
<div className='collection-item'>
<div
className='image'
style={{
backgroundImage: `url(${imgSource})`
}} />
</div>
</div>
)
EDIT
In the cases where the import depends upon the props, you could simple dynamically import the image within the function itself:
const CollectionItem = ({ item }) => (
import imgSource from `${item.imageUrl}`;
<div className='collection-item'>
<div
className='image'
style={{
backgroundImage: `url(${imgSource})`
}} />
</div>
</div>
)
In the case that it does not work, you can try using require instead
const imgSource = require(item.imageUrl);

React redux saga multiple renders

I'm new to react and I'm having an issue of multiple renders and I was just wondering if I'm doing this right, so I dispatched an action to get a note list, in my list component which looks like this for now :
import React, {useState, useEffect} from 'react';
export default function NoteList (props){
const [ noteList, updateNoteList ] = useState([]);
useEffect(()=>{
updateNoteList(
props.noteList.map(note => {
return {...note, mode : 'title-mode'};
})
)
},[props.noteList])
console.log(noteList);
return (
<div>
Notes come here
</div>
)
}
this component is connected in another container class but that's irrelevant, so what happens is this component renders 4 times, two times without the useEffect hook and two more with it, what I want to achieve is I need to add an item in the object of each note (which is mode : title-mode) in a state for this component which works fine with this code, as to why I'm adding this mode in a state is that I want to change this inside the note array so I can change the view mode for each note , but this component renders 4 times as I mentioned, and in my head no way that this is the correct way to do this.
Please help if you have the time .
We could achieve the display of the notes list by making a display-mode state in the <Note /> component it self so changing the mode won't affect other notes and this way we won't have extra re-renders, also using this approach will allow also modifying the note locally without dispatching it to the store then we could update the store at the end gaining in perfs.
So basically this is the approach (codesandbox):
const Note = ({ title, content }) => {
const [isExpanded, setIsExpanded] = useState(false);
return (
<div
style={{ border: "1px solid", margin: 5 }}
onClick={() => setIsExpanded(!isExpanded)}
>
{!isExpanded ? (
<div>
<h2>{title}</h2>
</div>
) : (
<div>
<h2>{title}</h2>
<p>{content}</p>
</div>
)}
</div>
);
};
function App() {
// this is the notes state, it could be coming from redux store so
// we could interact with it (modifying it if we need)
const [notes, setNotes] = React.useState([
{ id: 1, title: "note 1", content: "this is note 1" },
{ id: 2, title: "note 2", content: "this is note 2" }
]);
return (
<div className="App">
{notes.map((note) => (
<Note key={note.id} {...note} />
))}
</div>
);
}

can you set outer HTML in return function of a React component?

The default behavior of the MailchimpSubscribe component displays a status alert upon user signup. I want to move this status alert outside of it's position in the DOM so that it shows up at the top of the page.
My code looks like this:
import MailchimpSubscribe from "react-mailchimp-subscribe"
import SimpleForm from './SimpleForm.js'
function Info() {
return (
<div className="canary">
<MailchimpSubscribe url={process.env.REACT_APP_MAILCHIMP_URL}
render={({subscribe, status, message}) => <SimpleForm
status={status}
message={message}
className="form"
style={{}}
onSubmitted={formData => subscribe(formData)}
/>
}/>
</div>
);
}
export default Info;
To change the default button text, I create my own SimpleForm.js component:
import React from "react";
// a basic form
const SimpleForm = ({ status, message, className, style, onSubmitted }) => {
let input;
const submit = () =>
input &&
input.value.indexOf("#") > -1 &&
onSubmitted({
EMAIL: input.value
});
return (
<div className={className} style={style}>
{status === "sending" && <div style={{ color: "blue" }}>sending...</div>}
{status === "error" && (
<div
style={{ color: "red" }}
dangerouslySetInnerHTML={{ __html: message }}
/>
)}
{status === "success" && (
<div
style={{ color: "green" }}
dangerouslySetInnerHTML={{ __html: message }}
/>
)}
<input
ref={node => (input = node)}
type="email"
placeholder="Your email"
/>
<button onClick={submit}>Subscribe</button>
</div>
);
};
export default SimpleForm;
How would I modify this return function so that the dangerouslySetInnerHTML={{ __html: message }} part is set on <div className="canary">?
From what I see in component definition you can pass render props
MailchimpSubscribe.defaultProps = {
render: ({ subscribe, status, message }) => (
<SimpleForm
status={status}
message={message}
onSubmitted={formData => subscribe(formData)}
/>
)
};
with SimpleForm and include specific className style
<MailchimpSubscribe url={process.env.REACT_APP_MAILCHIMP_URL}
render={({subscribe, status, message}) => <SimpleForm
status={status}
message={message}
className="form"
style={{}}
onSubmitted={formData => subscribe(formData)}
/>
}/>
If you need more customization you can create your own component based on what's inside SimpleForm
This is how I understand the problem 😀:
Question
How do you render data passed as props to Component A within Component B?
MailchimpSubscribe holds message inside of its internal state and passes it as a prop to the results of its render function
Answer
React aims to only pass data from parents to children in unidirectional data flow,
so the data passed as props to MailchimpSubscribe cannot directly be rendered in another component which is not its child 😕
The best way I can think of to circumvent this is to leverage the Context API to create shared state between the SimpleForm rendered inside MailchimpSubscribe and a DisplayMessage rendered elsewhere in your app.
In a new file SharedState.js
Create SharedContext to allow components to access context
Create SharedProvider which is a parent that will make the context available to all its children
In SimpleForm
Read SharedContext
Set up an effect to push the status and message values up to the context. This effect will be called each time those values change.
return null when the status is not null, as you would like to render the message elsewhere and hide the form.
In DisplayMessage
Read SharedContext
Use status and message from SharedContext to render your message
Demo
I put together this CodeSandbox which illustrates this strategy.
References
React docs for useContext
React docs for Context
Hope that helps! Let me know if there's anything you'd like clarified further 👍

How to dismiss a React Modal using createPortal()?

Trying to dismiss a Modal dialog from within the Modal. I'm using ReactDOM.createPortal().
index.html
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<div id="modal"></div>
</body>
ResponseModal.js
import React from "react";
import ReactDOM from "react-dom";
// The gray background
const backdropStyle = {
...
};
// The modal "window"
const modalStyle = {
...
};
const MODAL_ROOT = document.querySelector("#modal");
const JSX_MODAL = ({ children, setShowResponses }) => (
<div style={backdropStyle}>
<div style={modalStyle}>
Child: {children}
<br />
<input
type="button"
value="Dismiss"
onClick={() => {
setShowResponses(false);
}}
/>
</div>
</div>
);
function Modal({ showResponses, ...props }) {
console.log("floop", showResponses);
if (showResponses) {
return ReactDOM.createPortal(JSX_MODAL(props), MODAL_ROOT);
}
return null;
}
export default Modal;
And finally, the containing component:
const LargeCell = ({ SCID, extra, fen, color }) => {
const [showResponses, setShowResponses] = useState(false);
return (
<div
style={{
...
}}
onClick={() => setShowResponses(true)}
>
<SmallCell {...{ SCID, color }} />
<DiagramForCell {...{ fen }} padding="3em"></DiagramForCell>
<span className="diff-text opening-text">{extra.opening.desc}</span>
<ResponsesModal {...{ showResponses, setShowResponses }}>
FLUM!
</ResponsesModal>
</div>
);
};
When I click on the LargeCell div, I see:
However, the dismiss button doesn't work. I'm sure that setShowResponses(false) is called, but there is no re-rendering of the Modal, so it is not dismissed. If I look at the Modal component in Chrome devtools, the state of showResponses still shows true.
So the question is: what is the correct way to dismiss this Modal?
So LargeCell was a table cell component, of which there were multiple. What I did was to push the Modal to the table level, and show/hide it from there:
{showResponses ? (
<ResponsesModal {...{ setShowResponses }}>FLUM!</ResponsesModal>
) : null}
setShowResponses is called by the Dismiss button in the Modal as shown previously.
The disadvantage is that to bring up the modal, each LargeCell needs setShowResponses, also. That prop has to be pushed several levels down. That's okay for now, but I'm starting to wonder if I should use a context.

'Unknown props' error when using react-tab-panel

I'm receiving an error when trying to implement the react-tab-panel in my application. I've followed the installation instructions to no avail.
This is the code which is producing the error:
import React from 'react';
import TabPanel from 'react-tab-panel'
const tabStyle = (props) => {
const baseStyle = {
padding: 10
}
return Object.assign(
baseStyle,
props.active?
{ color: 'red' }:
{ background: 'gray' }
)
}
const ExperimentDetail = ({ tabStyle }) => (
<div className="experiment-detail">
<TabPanel
tabAlign="center"
tabStyle={tabStyle}
>
<div tabTitle="first tab"> first </div>
<div tabTitle="second tab"> second </div>
<div tabTitle="third tab"> third </div>
</TabPanel>
</div>
);
export default ExperimentDetail;
The output is showing up as so, which is close to correct but with slight errors.
Thanks in advance for the help!
Reason is, you are using active props in TabStyle, but you are not passing in from ExperimentDetail component. One more thing you need to change, you need to use TabStyle name instead of tabStyle, because each react component must start with a uppercase.
And the main point is TabStyle is a react component, it will not return an object, it will return a html part so you can't use it with styling.
Use it in this way:
If you want to make a generic function that will return the style object, then write it in this way, create a separate file abc.js, put that function in that file, like this:
export function tabStyle(active){
const baseStyle = {
padding: 10
}
return Object.assign(
baseStyle,
props.active?
{ color: 'red' }:
{ background: 'gray' }
)
}
Import this function in your component:
import {tabStyle} from './abc /*path to this file*/';
Then use it in this way:
const ExperimentDetail = () => (
<div className="experiment-detail">
<TabPanel
tabAlign="center"
tabStyle={tabStyle(true)}
>
<div tabTitle="first tab"> first </div>
<div tabTitle="second tab"> second </div>
<div tabTitle="third tab"> third </div>
</TabPanel>
</div>
);
export default ExperimentDetail;
Check the working jsfiddle example: https://jsfiddle.net/ghoLrkjj/

Resources