I am trying to create a dynamically updating navbar in SvelteKit, with the currently open section formatted accordingly. I am attempting to identify the page based on the first part of the path, as below:
__layout.svelte:
<script context="module">
export const load = ({ page }) => {
return {
props: {
currentSection: `${page.path}`.split('/')[0],
sections: ['home', 'dashboard', 'settings']
}
};
}
</script>
<div class="min-h-screen bg-gray-100">
<Header {...props} />
<slot />
</div>
Header.svelte
<script>
import Menu from "$lib/nav/menu.svelte"
</script>
<Menu {...props}></Menu>
Menu.svelte
<script>
export let sections;
export let currentSection;
</script>
{#each sections as { section }}
<a
href="/{section}"
class="{section == currentSection
? 'bg-gray-900 text-white'
: 'text-gray-300 hover:bg-gray-700'} other-classes"
>{section}</a
>
{/each}
This is resulting in a props is not defined error, but I would have expected props to be defined since I've defined it in the return from the load() fundtion on the primary layout (based on the docs).
Do I somehow need to explicitly declare the props rather than expecting them to be available from the return of the load() function?
The props are passed from the module script to the regular component script, this means you still need to add export let props in your layout as well.
<script context="module">
export const load = () => {
return {
props: {
test: 123
}
}
}
</script>
<script>
export let test; //
</script>
Note that this will always spread out the props, you cannot do export let props and retrieve all the props, you can however get all the props passed to a component using $$props
Also, the load function is only available for pages and layouts, so you will for sure have to export props in both Header and Menu as those are just regular svelte components.
Related
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).
Problem
In React, we can pass functions as props into a child component when a function requires access to a state within the parent component of the aforementioned child. I was writing code for an application where I need such behavior to be implemented. However, I'm having trouble finding proper conventions for defining functions in Qwik, and then sending them through.
Attempt
I've tried defining the function within my interface to see if that helps Qwik to allow this implementation but so far that has not worked either.
Code
I'm trying to launch a modal from a icon contained in the header within my application. I'm trying to control displaying the modal by using a store declared within my header component. It's a Boolean value and determines if the modal would be displayed or not. I defined the function for modifying the state within my header component and attempted to pass it into my modal child component.
// components/header/header.tsx
import { component$, useClientEffect$, useStore } from "#builder.io/qwik";
import { strictEqual } from "assert";
import Modal from "../modal/modal";
export default component$(() => {
const store = useStore({
...
modal: false
});
function onClose() {
store.modal = false;
}
return (
<header>
{store.modal && <Modal onClose={onClose}/>}
<div
onClick$={()=>{
store.modal = true;
}}
>
<i class="fa-solid fa-cart-shopping"></i>
</div>
</header>
);
});
Inside my modal component I tried to use an interface to indicate that I'm passing a function into my props and tried to set as the function to execute within another icon contained within my child component.
// components/modal/modal.tsx
import { component$ } from "#builder.io/qwik";
import { strictEqual } from "assert";
interface ModalProps {
onClose: () => void
}
export default component$((props: ModalProps) => {
return (
<div>
<div>
<h1>Modal</h1>
<i onClick$={props.onClose}></i>
</div>
</div>
);
});
Error Message
When I click on the icon within my header, it displays the following error in my terminal.
log.js:10 QWIK ERROR Error: Code(3): Only primitive and object literals can be serialized at Array.flatMap (<anonymous>) ƒ onClose() { store.modal = false; }
Conclusion
Is there anyway to send functions as props into child components in Qwik JS?
If not, can I access stores contained in a parent component from within a child component?
Basically, what would be the ideal approach to solve this issue?
As I'm a noob like you in this framework, I've struggled to understand how this works too.
You actually need to pass a QRL as you may read here:
https://qwik.builder.io/docs/components/events/
So, here's how to modify your code for the Modal component:
import { component$, QRL } from '#builder.io/qwik';
interface ModalProps {
onClose: QRL<() => void>;
}
export default component$<ModalProps>(props => {
return (
<div>
<div>
<h1>Modal</h1>
<i onClick$={props.onClose}></i>
</div>
</div>
);
});
And your head component:
import { $, component$, useStore } from '#builder.io/qwik';
import Modal from '../components/test';
export default component$(() => {
const store = useStore({
modal: false
});
const onClose = $(() => {
store.modal = false;
});
return (
<header>
{store.modal && <Modal onClose={onClose} />}
<div
onClick$={() => {
store.modal = true;
}}
>
<i class="fa-solid fa-cart-shopping"></i>
</div>
</header>
);
});
I am getting **Unexpected token '&'** error while declaring globle variable .I also tried to stringily it's value but still getting same error why ?
I am doing like this.
<Head>
{
<script type="text/javascript">
var s_pageName2=JSON.stringify('abc:en-us:/lo-form')
</script>
}
</Head>
here is my whole code
https://codesandbox.io/s/long-bird-tgwcv?file=/pages/index.js
Ia m using nextjs framework . I want to create global variable having value this abc:en-us:/lo-form
This is not codesandbox error .I am facing same issue on my local machine
In jsx you cannot just do that, you can use dangerouslySetInnerHTML, but i highly discourage to use it.
<script
dangerouslySetInnerHTML={{
__html: ` var s_pageName2='abc:en-us:/lo-form'`
}}
/>
If you want to store a global variable to reuse in any of your components there are differents ways for achieve that, for example using react Context and expose a custom hook to use wherever you need to.
Example :
_app.js
import { MyProvider} from '../components/MyProvider'
function MyApp({ Component, pageProps }) {
return (
<MyProvider>
<Component {...pageProps} />
</MyProvider>
)
}
export default MyApp
/components/MyProvider :
import React, { useState, useEffect,useContext } from 'react'
export const MyContext= React.createContext(null)
export function MyProvider ({ children }) {
const [myVar, setMyVar] = useState('abc:en-us:/lo-form')
return (
<MyContext.Provider value={{myVar}}>
{children}
</MyContext.Provider>
)
}
export function useApp() {
const value = useContext(MyContext)
return value
}
Then in any other component / page you can just use your hook in this way :
import { useApp } from './components/MyProvider'
const MyPage= () => {
const {myVar} = useApp()
... rest of code
}
This is just an example, you can achieve that in many ways, it depends on your app business logic.
I have a simple react component that must load data from server when user ask it. Problem is that i don't know how to transfer dynamic variable speakerUrl and access it in before component load state. Sure i can access it from this.props.params, but component is not loaded and i can't access it when i make a graphql query. Query - QuerySpeaker is working fine when i manualy set url variable.
Router
<Route path="/speaker/:speakerUrl" component={SpeakerPage} />
Component - SpeakerPage
import React from 'react';
import { graphql } from 'react-apollo';
import { QuerySpeaker } from '../redux/graphql/querys';
class SpeakerPage extends React.Component {
render( ) {
console.log( this.props );
return (
<div className="ui container">
<div className="ui grid">
<div className="row">
Hello
</div>
</div>
</div>
)
}
}
export default graphql(QuerySpeaker, {
options: ({ url }) => ({ variables: { url } }) <- here is my problem, how can i set this url variable?
})( SpeakerPage );
Based on the react-apollo documentation, the argument passed to the options function is the props object passed to the component. When react-router renders your component, it passes it the params as a prop. That means that params should be a property of the object passed to the options function.
export default graphql(QuerySpeaker, {
options: (props) => ({ variables: { url: props.match.params.speakerUrl } })
})( SpeakerPage );
I'm new to React so please have mercy.
I've also read all of the threads on this, React / JSX Dynamic Component Name and React/JSX dynamic component names in particular. The solutions did not work.
I'm using a tab style interface where a user selects a tab and the appropriate content loads. A parent component stores the tab's content state, passes the corresponding props to the content child. This child then loads the correct content component (as its own child).
var TabbedContent = React.createClass({
loadMenu: function() {
var menus=this.props.carDivState.vehicleDetailState;
for (key in menus) {
if (menus.hasOwnProperty(key)) {
if (menus[key]) {
var Component='TabbedContent'+key;
return <Component />;
}
}
}
},
render: function() {
return (
<div className="TabbedContent">
<div className="contentWrapper">
{this.loadMenu()}
</div>
</div>
)
}
});
loadMenu loops through the props until it finds a true prop. It then returns that key (for instance "Overview") and creates a variable (e.g. Component='TabbledContentOverview').
However, my code returns an HTML tag <tabbedcontentoverview></tabbedcontentoverview>
Question
How do I get React to return the React component instead of an HTML tag? I appear to be using the correct capitalized naming conventions. I've read the Facebook docs. I just don't get it.
https://github.com/vasanthk/react-bits/blob/master/patterns/30.component-switch.md
import HomePage from './HomePage.jsx';
import AboutPage from './AboutPage.jsx';
import UserPage from './UserPage.jsx';
import FourOhFourPage from './FourOhFourPage.jsx';
const PAGES = {
home: HomePage,
about: AboutPage,
user: UserPage
};
const Page = (props) => {
const Handler = PAGES[props.page] || FourOhFourPage;
return <Handler {...props} />
};
// The keys of the PAGES object can be used in the prop types to catch dev-time errors.
Page.propTypes = {
page: PropTypes.oneOf(Object.keys(PAGES)).isRequired
};
First, if you are using Bootstrap for your app, I'd suggest that you use react-bootstrap`s tab. If you are not, I would suggest that you at least take a look at the implementation of their TabPane and TabbedArea.
Here's an example of how it looks like in your app:
const tabbedAreaInstance = (
<TabbedArea defaultActiveKey={2}>
<TabPane eventKey={1} tab='Tab 1'>TabPane 1 content</TabPane>
<TabPane eventKey={2} tab='Tab 2'>TabPane 2 content</TabPane>
<TabPane eventKey={3} tab='Tab 3' disabled>TabPane 3 content</TabPane>
</TabbedArea>
);
React.render(tabbedAreaInstance, mountNode);
Now, back to your question, if you want to create a component by name, just call React.createElement from inside your loadMenu:
loadMenu: function() {
var menus=this.props.carDivState.vehicleDetailState;
for (key in menus) {
if (menus.hasOwnProperty(key)) {
if (menus[key]) {
return React.createElement('TabbedContent'+key);
}
}
}
}
You need to have a reference to an actual class in order to create an element from it (in JS or JSX).
Hold a map of keys to React classes (i.e tabbedChildren), and just create this element using the JS API:
var childComponent = tabbedChildren[key]
return React.createElement(childComponent)
https://facebook.github.io/react/docs/top-level-api.html