Not able to render images in Gatsby from .yml file [duplicate] - javascript

I'm trying to display couple of images from array of objects containing images urls. I'm aware that StaticImage has to accept local variables as props values. These two variables for image urls are both local.
Why is this not working and is there a workaround?
import React from 'react';
import { StaticImage } from 'gatsby-plugin-image';
const TestPage = (props) => {
const itemData = [
{
img: 'https://images.unsplash.com/photo-1549388604-817d15aa0110?w=161&fit=crop',
title: 'Bed',
},
{
img: 'https://images.unsplash.com/photo-1563298723-dcfebaa392e3?w=161&fit=crop',
title: 'Kitchen',
},
];
const testSrc = 'https://images.unsplash.com/photo-1523413651479-597eb2da0ad6?w=161&fit=crop';
const testSrc2 = itemData[0].img;
return (
<>
<StaticImage src={testSrc} alt="works" />
<StaticImage src={testSrc2} alt="doesn't" />
</>
)
}
export default TestPage;

As you said, there's a restriction in the component:
Restrictions on using StaticImage
The images are loaded and processed at build time, so there are
restrictions on how you pass props to the component. The values need
to be statically-analyzed at build time, which means you can’t pass
them as props from outside the component, or use the results of
function calls, for example. You can either use static values, or
variables within the component’s local scope. See the following
examples:
In your case, a plain assignation works but an object assignation doesn't. Change it to:
import React from 'react';
import { StaticImage } from 'gatsby-plugin-image';
const TestPage = (props) => {
const testSrc = 'https://images.unsplash.com/photo-1523413651479-597eb2da0ad6?w=161&fit=crop';
const testSrc2 = 'https://images.unsplash.com/photo-1563298723-dcfebaa392e3?w=161&fit=crop';
return (
<>
<StaticImage src={testSrc} alt="Bed" />
<StaticImage src={testSrc2} alt="Kitchen" />
</>
)
}
export default TestPage;

Related

How to use Preact signals with astro to share state

Problem
When i change the tag value it only changes on the select component but not in the index.astro
I have folder signals where i export signal
export const tagSignal = signal<string>("all");
I use it like this in Select.tsx component, and here evryting changes
import { tagSignal } from "#signal/*";
const setTagValue = (value: string) => {
tagSignal.value = value;
console.log("select", tagSignal.value);
};
export const Select = () => {
const [display, setDisplay] = useState(false);
const [selectedName, setSelectedName] = useState("all"); // this will be change to only signals still under refator
setTagValue(selectedName);
-------
------
but when I import it to index.astro like this I get only "all" value witch is inital value
---
import { Icon } from "astro-icon";
import { Picture } from "astro-imagetools/components";
import Layout from "#layouts/Layout.astro";
import { Select } from "#components/Select";
import Card from "#components/Card.astro";
import { getCollection } from "astro:content";
import { getProjectsByTag } from "#utils/*";
import { tagSignal } from "#signal/*";
const projects = await getCollection("projects");
const filteredProjects = getProjectsByTag(projects, tagSignal.value);
// TODO: add links
console.log("index", tagSignal.value);
---
/// some code here
<section id="projects" class="projects">
<Select client:only="preact" />
<div class="projects-wrapper">
{
filteredProjects.map(({ data: { title, heroImage } }) => (
<Card name={title} bg_path={heroImage} />
))
}
</div>
</section>
---
I see two issues here.
You are depending on dynamic JS variables in an .astro file. It doesn't work the way you are expecting—all the javascript in .astro files, with the exception of the "islands," e.g., your Select.tsx component, is being evaluated when the page is being built. So Astro grabs the initial value of tagSignal, but makes it a static string.
People can get bitten by, e.g., the trivial ©2010—{new Date().getFullYear()} in the footer – it won't magically update on the new year's eve if used in .astro file.
The state (signal's current value) is not shared accross the islands. If you want to share it, you need either a global state solution (I haven't used it in Astro yet), or just create a common parent for the Select and the filtering logic, e.g.:
{/* the signal will be declared (or imported) in `DynamicSection`*/}
<DynamicSection client:only="preact">
<Select />
<div class="projects-wrapper">
{
filteredProjects.map(({ data: { title, heroImage } }) => (
<Card name={title} bg_path={heroImage} />
))
}
</div>
</ DynamicSection>
(The simplest global state solution would be probably using the url with a query string, and derive the state from its value).

Access Object Values with Context API in Child Component

I have the following object being exported into another file:
info.js
export const info = {
companyName: '',
title: 'Some title',
year: '',
};
I'm importing this object into my Context.js like so:
InfoContext.js
import React, { useState, createContext } from 'react';
import {info} from './info'
export const InfoContext = createContext();
export const InfoProvider = ({ children }) => {
const [state, setState] = useState({
...info,
});
return (
<InfoContext.Provider value={[state, setState]}>
{children}
</InfoContext.Provider>
);
};
What I want to do is access the object values from my state inside my App.js. - Here is what I have tried but I am not having any success:
App.js
import React from "react";
import { InfoProvider, InfoContext } from "./InfoContext";
export default function App() {
return (
<InfoProvider>
<InfoContext.Consumer>
{state => (
<>
<h1>{state.title}</h1>
</>
)}
</InfoContext.Consumer>
</InfoProvider>
);
}
I'm clearly missing something obvious here. I've tried a few things but I'm not sure what the issue is. I feel it has something to do with my object being accessed from a separate file.
Additionally, here is a sandbox link with the above code. Any help would be greatly appreciated!
Code Sandbox
You are passing your value to Provider as array, but on Consumer you expecting it to be an Object.
You need to pass an Object instead:
<InfoContext.Provider value={{state, setState}}>
Also you are using Consumer wrong. As a callback it takes whole value that you've passed in Provider, not state:
<InfoContext.Consumer>
{(value) => (
<>
<h1>{value.state.title}</h1>
</>
)}
</InfoContext.Consumer>
or using destructured assignment:
<InfoContext.Consumer>
{({state}) => (
<>
<h1>{state.title}</h1>
</>
)}
</InfoContext.Consumer>
then you can use value.setState({...}) for example. etc. But note that this is a bad practice updating state like that.
Code Sandbox

Trouble accessing and displaying JSON using ReactJS and the fetch-api

everyone thanks for the help!
I have looked at several similar questions but have not been able to extrapolate their answers to solve my problem.
I am using a ReactJS application to consume JSON from a website. I'm using the code from https://pusher.com/tutorials/consume-restful-api-react and changing it to fit my situation.
Currently, when I view index.js, I get the error "TypeError:
assetList.assets is undefined." Given the the JSON and code below, what do I need to change to
display a list of the assets and their properties?
I would like something like the display to look like the Desired Display below.
Desired Display.
There are two 2 assets:<br/>
id: 1317 Filename: PROCESS_FLOW.pdf
id: 1836 Filename: 004527_FS.jpg
JSON consumed from website
{"totalNumberOfAssets":2,
"assets":[
{"id":"1317","attributes":{"Filename":["PROCESS_FLOW.pdf"]}},
{"id":"1836","attributes":{"Filename":["004527_FS.jpg"]}}
]}
components/assetList.js
import React from 'react'
const AssetList = ({assetList}) => {
return (
<div>
There are {assetList.totalNumberOfAssets} assets:
{assetList.assets.map((asset) => (
<div>
id: {asset.id} filename: {asset.filename}
</div>
))}
</div>
)
};
export default AssetList
App.js
import React, {Component} from 'react';
import AssetList from './components/assetList';
class App extends Component {
render() {
return (
<AssetList assetList={this.state.assetList} />
)
}
state = {
assetList: []
};
componentDidMount() {
fetch('http://ligitaddress/api/v1/asset')
.then(res => res.json())
.then((data) => {
this.setState({ assetList: data })
})
.catch(console.log)
}
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
On your first render, the value of this.state.assetList is an array:
state = {
assetList: []
};
However you are passing it into <AssetList>
const AssetList = ({assetList}) => {
return (
<div>
There are {assetList.totalNumberOfAssets} assets:
{assetList.assets.map((asset) => (
<div>
id: {asset.id} filename: {asset.filename}
</div>
))}
</div>
)
};
The line saying assetList.assets.map is trying to call map() on something that is undefined. (you can access the property assets on an array and it will be undefined) It seems like it expects assetList to be an object with an assets array in it, but in your parent component assetList is initialized to an array... in short you're confusing yourself as to what kind of data you expect to be where.
Either change your initial state to reflect how you expect it to be passed into <AssetList>:
state = {
assetList: {
assets: []
}
};
And/or change your <AssetList> component to properly check its prop:
const AssetList = ({assetList}) => {
return (
<div>
There are {assetList.totalNumberOfAssets} assets:
{Array.isArray(assetList.assets) && assetList.assets.map((asset) => (
<div>
id: {asset.id} filename: {asset.filename}
</div>
))}
</div>
)
};
This is happening because your components/assetList.js is trying to access assetList.assets on assetList.assets.map without it being defined.
When the API request is made and has not returned yet, the assets on assetList have not being defined, since assetList on App.js is initialized to an empty array.
You can replace the line on components/assetList.js with assetList.assets && assetList.assets.map(...) and that should do it

Using provider directly from context vs returning it from a function

I'm noticing something new I haven't seen before. It is possible that this isn't specific to this react component.
I tried creating a react context
const MyContext = createContext({...});
Then, I wrote a function to return the provider
const MyProvider = () => {
return <MyContext.Provider value={...} />;
};
<MyProvider /> is a function type React component and <MyContext.Provider /> is an object type React component
When <MyProvider /> is used to wrap components, the React app crashes. However, directly using <MyContext.Provider /> works like I expected.
Since those two aren't the same, is it possible to create a provider component externally and import it elsewhere to use it?
export const MyProvider = (props) => { return ( <MyContext.Provider value={...} /> { props.children } </MyProvider> ); };
We can import it like :
import { MyProvider } from '../path'
use
<MyProvider><YouComponent/></MyProvider>

How to get a new instance of an exported object on each import?

I have this file that I'm keeping a INITIAL_VALUE for a form field, that I'm building.
INITIAL_VALUE.js
const INITIAL_VALUE = [];
export default INITIAL_VALUE;
And the problem is that INITIAL_VALUE is an array. A non-primitive, that is handled by reference.
Component1.js
import INITIAL_VALUE from "./INITIAL_VALUE";
import React, { useState } from "react";
function Component1(props) {
const [myState, setMyState] = useState(INITIAL_VALUE);
const [boolean, setBoolean] = useState(false);
function handleClick() {
setMyState(prevState => {
prevState.push(1);
return prevState;
});
setBoolean(prevState => !prevState);
props.forceUpdateApp();
}
return (
<React.Fragment>
<div>This is my state: {JSON.stringify(myState)}</div>
<button onClick={handleClick}>Modify State Comp1</button>
</React.Fragment>
);
}
export default Component1;
Component2.js
The same as Component1, but it's named Component2 and it has its own file.
App.js
function App() {
const [boolean, setBoolean] = useState(false);
function forceUpdateApp() {
setBoolean(prevState => !prevState);
}
return (
<React.Fragment>
<Component1 forceUpdateApp={forceUpdateApp} />
<Component1 forceUpdateApp={forceUpdateApp} />
<Component2 forceUpdateApp={forceUpdateApp} />
</React.Fragment>
);
}
CodeSandbox
PROBLEM
Component1.js and Component2.js both import the INITIAL_VALUE file. And I was under the impression that, each one of these imports would get a brand new instance of the INITIAL_VALUE object. But that is not the case as we can see from the GIF below:
QUESTION
Is there a way to keep an array as a initial value living declared and imported from another file and always get a new reference to it on each import? Is there another pattern I can use to solve this? Or should I stick with only primitive values and make it null instead of [] and intialize it in the consumer file?
Is there a way to keep an array as a initial value living declared and imported from another file and always get a new reference to it on each import?
No, that's not possible. The top-most level code of a module will run once, at most. Here, the top level of INITIAL_VALUE.js defines one array and exports it, so everything that imports it will have a reference to that same array.
Easiest tweak would be to export a function which creates the array instead:
// makeInitialValue.js
export default () => {
const INITIAL_VALUE = [];
// the created array / object can be much more complicated, if you wish
return INITIAL_VALUE;
};
and then
import makeInitialValue from "./makeInitialValue";
function Component1(props) {
const INITIAL_VALUE = makeInitialValue();
const [myState, setMyState] = useState(INITIAL_VALUE);
In the simplified case that you just need an empty array, it would be easier just to define it when you pass it to useState.
All that said, it would be much better to fix your code so that it does not mutate the existing state. Change
setMyState(prevState => {
prevState.push(1);
return prevState;
});
to
setMyState(prevState => {
return [...prevState, 1];
});
That way, even if all components and component instances start out with the same array, it won't cause problems, because the array will never be mutated.

Categories