I'm experiencing an error "InternalError: too much recursion" when trying to push from my Layout to a Post site.
Code of Layout.vue:
watch(searchText, (newValue, oldValue) => {
log('Current State of SearchText', newValue);
if (newValue !== null) {
if (newValue.value !== undefined) {
let id = newValue.value;
// push to post with id
router.push(`/post/${id}`);
} else {
// todo: start search
}
}
});
I'm using the watch to react when my QSelect model value is changing.
My route:
{ path: '/post/:id', component: () => import('pages/Post.vue'),
My Post-page:
<template>
<q-page class="">
<Post // I'm getting no error when deleting this Component
:description="post.description"
:title="post.title"
:author="post.user"
:date="post.date"
:tags="post.tags"
:commentArray="post.comments"
/>
<h1>
Test
</h1>
</q-page>
</template>
<script>
import Post from 'src/components/PostComp.vue';
import { useRouter, useGetters, useActions } from '#u3u/vue-hooks';
import { ref } from '#vue/composition-api';
import moment from 'moment';
const debug = require('debug');
const log = debug('app:PostPage');
export default {
name: 'Post',
components: {
Post,
},
setup() {
const { route } = useRouter();
const post = ref({});
const getters = {
...useGetters('post', ['post']),
};
const actions = {
...useActions('post', ['findAll']),
};
log('params:', route.value.params);
const p1 = getters.post.value(route.value.params.id);
post.value = {
title: p1[0].title,
user: 'Mary Sullyman',
description: p1[0].description,
tags: p1[0].postTags,
comments: p1[0].commentDTOList,
date: moment(p1[0].createdDate).format('DD.MM.YYYY HH:mm') + ' Uhr',
};
log(post);
What I'm trying to do:
I have a QSelect in my Toolbar to search for posts which works just fine. Now I'm trying to push to a dynamically generated site for the post clicked.
What if you remove name: 'Post' (from export default)? The name you set matches the component tag name, so it falls into an infinite render loop.
See Recursive Components (still applies to Vue 3 even though it's from Vue 2 docs)
Related
How can I access a method or variable defined in the Vuex 4 store in my Blade file?
I am using a compiled app.js from vite. Obviously in the components of Vue js I can access the store, I wonder if it is also possible to do it in a blade file.
Vue js #app instance must be one of course.
If at the end of my blade file I write this
<script>
import {useStore} from "vuex";
import {onMounted, watch, ref, defineComponent} from 'vue'
export default {
setup() {
const click = () => {
store.commit('mutazione');
};
onMounted(() => {
alert('test');
})
const store = useStore();
return {
store,
click
}
},
}
</script>
the console gives me this type of error
Unexpected token '{'. import call expects exactly one argument.
First Leave laravel out of it.
It is purely vuex5.0 / Pinia https://pinia.vuejs.org job to do.
Best practice is Create the store of every type of data you need like
https://github.com/puneetxp/the check test directly in it totally in JavaScript you can see like create Store assume it is for Product data.
import { defineStore, acceptHMRUpdate } from "/js/vue/pinia.js";
export const useProductStore = defineStore({
id: "Product",
state: () => ({
rawItems: [],
}),
getters: {
items: (state) => state.rawItems
},
actions: {
addItem(product) {
this.rawItems.push(product)
},
removeItem(id) {
this.rawItems = this.rawItems.filter(i => i.id != id);
},
editItem(product) {
this.rawItems = this.rawItems.filter(i => i.id != product.id);
this.rawItems.push(product);
},
upsertItem(products) {
products.forEach(product => {
this.rawItems = this.rawItems.filter(i => i.id != product.id);
this.rawItems.push(product);
});
}
},
})
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useActive_roleStore, import.meta.hot))
}
then we can use in your component
import { useProductStore } from "/js/vue/Store/Model/Product.js";
export default {
template: ``,
data() {
return {
useProductStore,
}
}
}
for use use it on useProductStore().items as my design you can make your own if you want.
I'm learning how to make a single page app with javascript.
My javascript teacher provided a beautiful tutorial how to create a single page application from scratch. I followed the tutorial and everything went well untill the part where the routing came in..
He uses a library which is called navigo. I don't know why but it seems to not working for me at all.
The moment I've written the final line of code. My homepage disappeared and the console gave a warning that my route '/' which is my homepage, didn't match any of the registered routes, but it looks like there is no route registered at all, while I'm definitly registering them..
here is my code
My root index.js
import './sass/main.scss';
import App from './App';
import { HomeComponent, NewEventComponent } from './Components';
// Retrieve appComponent
const initApp = () => {
const appContainer = document.getElementById('appContainer');
const app = new App(appContainer);
app.addComponent(new HomeComponent());
app.addComponent(new NewEventComponent());
};
window.addEventListener('load', initApp);
My App.js (here is where my route is defined for every component. routerPath makes it dynamic )
// The App Wrapper
import Component from './lib/Component';
import Router from './Router';
class App {
constructor(parent) {
this.parent = parent;
this.components = [];
}
clearparent() {
while (this.parent.firstChild) {
this.parent.removeChild(this.parent.lastChild);
}
}
addComponent(component) {
if (!(component instanceof Component)) return;
// get the name from our component
const { name, routerPath } = component;
// when a component asks to reRender
component.reRender = () => this.showComponent(component);
// add to internal class
this.components.push(component);
// add to router
Router.getRouter().on(routerPath, () => {
this.showComponent({ name });
}).resolve();
}
showComponent({ name }) {
const foundComponent = this.components.find((component) => component.name === name);
if (!foundComponent) return;
this.clearparent();
this.parent.appendChild(foundComponent.render());
}
}
export default App;
The Home Component
// The Home Component
import Component from '../lib/Component';
import Elements from '../lib/Elements';
class HomeComponent extends Component {
constructor() {
super({
name: 'home',
model: {
counter: 0,
},
routerPath: '/',
});
}
incrementCounter() {
this.model.counter += 1;
}
render() {
const { counter } = this.model;
// create home container
const homeContainer = document.createElement('div');
// append header
homeContainer.appendChild(
Elements.createHeader({
textContent: `Current value is: ${counter}`,
}),
);
// append button
homeContainer.appendChild(
Elements.createButton({
textContent: 'increase',
onClick: () => { this.incrementCounter(); },
}),
);
return homeContainer;
}
}
export default HomeComponent;
A Component
// My components
class Component {
constructor({
name,
model,
routerPath,
}) {
this.name = name;
this.model = this.proxyModel(model);
this.routerPath = routerPath;
this.reRender = null;
}
proxyModel(model) {
return new Proxy(model, {
set: (obj, prop, value) => {
obj[prop] = value;
if (this.reRender) this.reRender();
return true;
},
});
}
}
export default Component;
The Router
// My Router
import Navigo from 'navigo';
const Router = {
router: null,
getRouter() {
if (!this.router) {
const rootUrl = `${window.location.protocol}//${window.location.host}`;
this.router = new Navigo(rootUrl, false);
}
return this.router;
},
};
export default Router;
Solution: I switched to Navigo(^7.0.0) and it works!
I seem to have the same problem as you. I'm also using navigo (^8.11.1). The problem is fixed for me when I declare a new router like this: new Navigo('/', false).
It still gives me the warning now, but it loads the page. sadly, this will only work in a dev environment
I am learning to react-redux-firebase with the tutorial below.
I added sub-collection to collection.
However, I don't know how to fetch subcollection.
I need to get the original collection and its subcollection.
tutorial:
https://github.com/iamshaunjp/React-Redux-Firebase-App/blob/lesson-20/marioplan/src/components/projects/ProjectDetails.js
const mapStateToProps = (state, ownProps) => {
const id = ownProps.match.params.id;
const projects = state.firestore.data.projects;
const project = projects ? projects[id] : null
const subproject = //?
}
export default compose(
connect(mapStateToProps),
firestoreConnect([
{ collection: 'projects',
collection: 'subprojects'
}
])
)(ProjectDetails);
I would like to use this
not
Edited answer:
I dont post all the steps here, but you need to follow the exact same steps you did with the projects collection, but for your subprojects collection, so make your subprojects reducer, add that to the rootReducer...etc, so that the subprojects are in the global state, and you can link those to your component, the same as you did with the projects collection.
You would need to define if it will be in a separated reducer, in the project reducer itself so that it will figure in the state like this: const subprojects = state.firestore.data.projects.subprojects;
To compose your connector, you need to check redux-firestore repo where its adviced how to connect subcollections. Check the exmaple:
{
collection: 'cities',
doc: 'SF',
subcollections: [{ collection: 'zipcodes' }],
storeAs: 'SF-zipcodes' // make sure to include this
},
So that said, the mapStateToProps would be as below:
const mapStateToProps = (state, ownProps) => {
// console.log(state);
const id = ownProps.match.params.id;
const subprojects = state.firestore.data.projects.subprojects;
const subproject = subproject ? subproject [id] : null
return {
subproject : subproject
}
}
export default compose(
connect(mapStateToProps),
firestoreConnect([{
collection: 'projects',
doc: 'YourDoc'
subcollections: [{ collection: 'subprojects' }],
storeAs: 'YorDoc-subprojects'
}])
)(YourSubProjectsComponent)
Not debugged code, just a proposal based on the repo example in case it might be helpfull for you to move on.
On the other hand I case if it is of any help, find below the code of the projectDetails compenent, fetching the determined project from the firestore with the useState hook. For your specific case, I believe that it would apply the same way, but querying your subprojects collection. (typescript code). I post this, just in case you might be after a 'manual' fetch of your collection, instead of the direct binding with redux. If what you want is the direct bind with redux of your subproject collection you just can ommit the code below.
import { IFirebaseProject } from '../../store/types/projectTypes';
import { Redirect } from 'react-router-dom';
import moment from 'moment';
import firebase from 'firebase/app';
import { useState } from 'react';
async function getFbProject(id: string) {
const db = firebase.firestore();
const fbDoc = db.collection("projects").doc(id);
let project = {} as IFirebaseProject;
await fbDoc.get().then(function(doc) {
if (doc.exists) {
console.log("Document data:", doc.data());
project = { ...doc.data()} as IFirebaseProject;
} else {
console.log(`Document does not exist ${id}`);
}
}).catch(function(error) {
console.log(`Error getting document: ${id}`, error);
});
return project;
}
function getProjectId():string {
const pathStr = window.location.pathname.toString();
const parts = pathStr.split("/");
const projStrIndex = parts.indexOf('project');
const projectId = parts[projStrIndex + 1];
return projectId;
}
const ProjectDetails = ({ project }: { project: IFirebaseProject } | { project: undefined }) => {
const [stateProject, setStateProject] = useState<IFirebaseProject | undefined>(project);
if (!firebase.auth().currentUser) return <Redirect to='/'/>
if (stateProject) {
return(
<div className="container section project-details">
<div className="card z-depth-0">
<div className="card-content">
<span className="card-title">{stateProject.title}</span>
<p>
{stateProject.content}
</p>
</div>
<div className="card-action grey lighten-4">
<div>{stateProject.authorFirstName} {stateProject.authorLastName}</div>
<div>{moment(stateProject.createdAt.toDate()).calendar()}</div>
</div>
</div>
</div>
)
} else {
//fetch project
const projectId = getProjectId();
getFbProject(projectId).then((project) => {
if (project) {
setStateProject(project);
}
});
return(
<div>
<p> Loading project... </p>
</div>
)
}
}
export default ProjectDetails;
Ok, I got a head scratcher I need a little bit of help with. The setup is that I have React/Redux app with a Categories page that reads a list of categories from an API, then lists them out. That part works fine. What I'm trying to do is pass in an event handler to each of the category child components that, when clicked, dispatches an action that toggles the state of the component, i.e., if the category is selected and clicked on, it will "unselect" it (which actually means deleting an entry from a database table called user_category), and if not selected, will "select" that category for that user (add an entry in the user_category table).
So I've got an onclick handler (handleCatClick) that is supposed to ultimately pass a categoryId and a userId to perform these operations. Unfortunately what I'm finding that even though these arguments are being passed to the function, they end up being undefined. So I'm not sure if I'm passing this function correctly or what exactly I've missed.
Everything works other than this - maybe you can help me spot the problem ;-)
Click here to view the database layout
Click here to see how the category page looks
The applicable pages in my app:
The architecture looks basically like this:
/views/[Categories]
- index.js (wrapper for the Categories Component)
- CategoriesComponent.jsx (should be self-explanatory)
[duck]
- index.js (just imports a couple of files & ties stuff together)
- operations.js (where my handleCatClick() method is)
- types.js (Redux constants)
- actions.js (Redux actions)
- reducers.js (Redux reducers)
[components]
[Category]
- index.jsx (the individual Category component)
/views/index.js(main Category page wrapper)
import { connect } from 'react-redux';
import CategoriesComponent from './CategoriesComponent';
import { categoriesOperations } from './duck'; // operations.js
const mapStateToProps = state => {
// current state properties passed down to LoginComponent (LoginComponent.js)
const { categoryArray } = state.categories;
return { categoryArray }
};
const mapDispatchToProps = (dispatch) => {
// all passed in from LoginOperations (operations.js)
const loadUserCategories = () => dispatch(categoriesOperations.loadUserCategories());
const handleCatClick = () => dispatch(categoriesOperations.handleCatClick());
return {
loadUserCategories,
handleCatClick
}
};
const CategoriesContainer = connect(mapStateToProps,mapDispatchToProps)(CategoriesComponent);
export default CategoriesContainer;
/views/CategoriesComponent.jsx (display layer for the Categories view)
import React from 'react';
import {Row,Col,Container, Form, Button} from 'react-bootstrap';
import {Link} from 'react-router-dom';
import './styles.scss';
import Category from './components/Category';
import shortid from 'shortid';
class CategoriesComponent extends React.Component {
constructor(props) {
super(props);
this.loadUserCats = this.props.loadUserCategories;
this.handleCatClick = this.props.handleCatClick;
}
componentWillMount() {
this.loadUserCats();
}
render() {
return (
<Container fluid className="categories nopadding">
<Row>
<Col xs={12}>
<div className="page-container">
<div className="title-container">
<h4>Pick your favorite categories to contine</h4>
</div>
<div className="content-container">
<div className="category-container">
{
this.props.categoryArray.map((item) => {
return <Category className="category" handleClick={this.props.handleCatClick} key={shortid.generate()} categoryData={item} />
})
}
</div>
</div>
</div>
</Col>
</Row>
</Container>
)
}
}
export default CategoriesComponent
/views/Categories/components/index.jsx (Single Category Component)
import React from 'react';
import {Row,Col,Container, Form, Button} from 'react-bootstrap';
import './styles.scss';
import Img from 'react-image';
class Category extends React.Component {
constructor(props) {
super(props);
this.state = {
categoryName: this.props.categoryData.category_name,
categoryImg: this.props.categoryData.category_img,
categoryId: this.props.categoryData.category_id,
userId: this.props.categoryData.user_id,
selected: this.props.categoryData.user_id !== null,
hoverState: ''
}
this.hover = this.hover.bind(this);
this.hoverOff = this.hoverOff.bind(this);
this.toggleCat = this.toggleCat.bind(this);
}
toggleCat() {
// the onClick handler that is supposed to
// pass categoryId and userId. When I do a
// console.log(categoryId, userId) these two values
// show up no problem...
const {categoryId, userId} = this.state;
this.props.handleClick(categoryId, userId);
}
hover() {
this.setState({
hoverState: 'hover-on'
});
}
hoverOff() {
this.setState({
hoverState: ''
});
}
render() {
const isSelected = (baseCat) => {
if(this.state.selected) {
return baseCat + " selected";
}
return baseCat;
}
return (
<div className={"category" + ' ' + this.state.hoverState} onClick={this.toggleCat} onMouseOver={this.hover} onMouseOut={this.hoverOff}>
<div className={this.state.selected ? "category-img selected" : "category-img"}>
<Img src={"/public/images/category/" + this.state.categoryImg} className="img-fluid" />
</div>
<div className="category-title">
<h5 className={this.state.selected ? "bg-primary" : "bg-secondary"}>{this.state.categoryName}</h5>
</div>
</div>
);
}
}
export default Category;
/views/Categories/duck/operations.js (where I tie it all together)
// operations.js
import fetch from 'cross-fetch';
import Actions from './actions';
import Config from '../../../../config';
const loadCategories = Actions.loadCats;
const selectCat = Actions.selectCat;
const unSelectCat = Actions.unSelectCat;
const localState = JSON.parse(localStorage.getItem('state'));
const userId = localState != null ? localState.userSession.userId : -1;
const loadUserCategories = () => {
return dispatch => {
return fetch(Config.API_ROOT + 'usercategories/' + userId)
.then(response => response.json())
.then(json => {
dispatch(loadCategories(json));
});
}
}
const handleCatClick = (categoryId, categoryUserId) => {
// HERE IS WHERE I'M HAVING A PROBLEM:
// for whatever reason, categoryId and categoryUserId
// are undefined here even though I'm passing in the
// values in the Category component (see 'toggleCat' method)
var params = {
method: categoryUserId !== null ? 'delete' : 'post',
headers: {'Content-Type':'application/json'},
body: JSON.stringify(
{
"category_id": categoryId,
user_id: categoryUserId !== null ? categoryUserId : userId
}
)
};
const toDispatch = categoryUserId !== null ? unSelectCat : selectCat;
return dispatch => {
return fetch(Config.API_ROOT + 'usercategories/', params)
.then(response => response.json())
.then(json => {
dispatch(toDispatch(json));
});
}
}
export default {
loadUserCategories,
handleCatClick
}
The problem that I am having:
So I'm thinking I'm either not referencing handleCatClick correctly, or I'm somehow not passing the categoryId and userId correctly so that when it finally gets to handleCatClick(categoryId, categoryUserId) in operations.js, it ends up as undefined. It's probably something simple but I can't spot it. NOTE: I haven't included files like the types.js or reducers.js, because they seem to be outside the scope of the problem, but if you need them please let me know. Thanks in advance for your help!
Try this changes: Add params to these handlers
const handleCatClick = (categoryId, categoryUserId) => dispatch(categoriesOperations.handleCatClick(categoryId, categoryUserId));
and
return <Category className="category" handleClick={(categoryId, categoryUserId) => this.props.handleCatClick(categoryId, categoryUserId)} key={shortid.generate()} categoryData={item} />
As shown below I'm getting my data in my nextJS application in the pages/article.js using a graphQL query.
This data is passed down to another react component, which gives me a list of checkboxes.
Selecting a checkbox is calling a mutation to store the ID of the selected checkboxes in the DB.
To get the content updated, I'm using refetchQueries to call the main query again, which will pass the data down to the current component.
So far everything is working. Now I would like to get this stuff realtime using optimistic UI - which makes me some problems...
Replacing the refetchQueries with
update: (store, { data: { getArticle } }) => {
const data = store.readQuery({
query: getArticle,
variables: {
id: mainID
}
})
console.log(data)
}
runs me to the error TypeError: Cannot read property 'kind' of undefined which comes from readQuery.
I don't see what I'm doing wrong. And this is just the first part to get optimisic UI..
pages/article.js
import Article from '../components/Article'
class ArticlePage extends Component {
static async getInitialProps (context, apolloClient) {
const { query: { id }, req } = context
const initProps = { }
// ...
return { id, ...initProps }
}
render () {
const { id, data } = this.props
const { list } = data
return (
<Article
mainID={id}
list={list}
/>
)
}
}
export default compose(
withData,
graphql(getArticle, {
options: props => ({
variables: {
id: props.id
}
})
})
)(ExtendedArticlePage)
components/Article.js
import { getArticle } from '../graphql/article'
import { selectMutation } from '../graphql/selection'
export class Article extends Component {
checkboxToggle (id) {
const { mainID, checkboxSelect } = this.props
checkboxSelect({
variables: {
id
},
refetchQueries: [{
query: getArticle,
variables: {
id: mainID
}
}],
})
}
render () {
const { list } = this.props
return (
list.map(l => {
return (<Checkbox onClick={this.checkboxToggle.bind(this, l.id)} label={l.content} />)
}
)
}
}
export default compose(
graphql(selectMutation, { name: 'checkboxSelect' })
)(Article)
You have a variable shadowing issue in your update code, it seems that you're using the same name getArticle for both your query and the mutation result nested in data.
This is why your call to readQuery fails, the query params you need to provide resolves to the mutation result and not the actual query, hence the TypeError: Cannot read property 'kind' of undefined.
You just need to name your query with another identifier like getQueryArticle.