How can I access an element (of a custom component) by its 'id' within its parent? (ReactJS.Net) - reactjs.net

I expect the following is excessively easy, which probably plays into why I've had trouble finding the solution on the web:
I would like to reference elements made from my custom components by there IDs using document.getElementById("id") from within a parent component.
I have created a couple of react components, one of which includes the other in the render function. I have given the inner element (I will refer to this as 'c1', and the outer as 'c0') an id="myID", and I would like to reference it using document.getElementById("myID").myValue. However, whenever I try to access it this way, I receive the error message TypeError: Unable to get property 'myValue' of undefined or null reference. C1 does have a property 'myValue', although I don't think that should matter to this particular error.
This effectively the pair of components, simplified to the relevant parts:
import React, {Component} from 'react';
import {c1} from './c1.js';
export class c0 extends Component
{
displayName = c0.name
constructor(props)
{
super(props);
this.state = {
testValue: ""
}
{
componentDidUpdate() {
this.setState({
testValue: document.getElementById("myID").id
});
}
render() {
return (
<div id="testID">
<c1 id="myID" />
</div>
);
}
}
import React, {Component} from 'react';
export class c1 extends Component
{
displayName = c1.name
constructor(props)
{
super(props);
{
render() {
return (
<div>
<p>Here is some content</p>
</div>
);
}
}
To be clear, c1 is rendering from within c2. I just can't access it from the document for some reason.
My searches have basically resulted in non-reactjs, explanations for what is "better" than using document.getElementById(), and questions that assume I've already gotten this far. The last is understandable, but not very helpful to me. Any help would be greatly appreciated.
Edit:
Upon further inspection, the component seems to render exclusively as the set of html elements it contains. In the sample provided, document.getElementById("testID").firstChild returns HTMLDivElement. Specifically, it returns whichever HTMLElement is outermost in c1's render() function. I assume this is normal to some degree, but I wouldn't know for sure. Directly accessing by 'id' still returns null.

I did manage to find a workable solution. However, the following may not be sufficient for all users, so bear that in mind.
Within the render function of c1, I assigned the outermost <div> the same id as c1 itself, like so:
<div id={this.props.id}>.
It feels a bit patchwork, but it does the job. From this point, the properties of c1 can be changed or accessed when necessary, so long as those properties have also been assigned to that outermost <div>.

Related

Action decorator in Mobx does not function with strict-mode

I just started learning about Mobx to implement it in my projects, and I've come across a big issue: I seem to not understand how actions work.
I've been following this nice tutorial: https://hackernoon.com/how-to-build-your-first-app-with-mobx-and-react-aea54fbb3265 (the complete code of the tutorial is located here: https://codesandbox.io/s/2z2r43k9vj?from-embed ), and it works smoothly. I've tried to do a small React App on my side, trying to do the same the tutorial mentioned, and yet it is failing. I am sure there is some small detail (since the app is pretty simple) that I am not seeing, so I would appreciate some help on it.
I've also tried to look for similar cases to mine, but I didn't find anything through a quick search (which makes me think even more the problem is insignificant...)
My code is this:
import React, { Component } from 'react';
import { decorate, observable, action, configure } from 'mobx';
import { observer } from 'mobx-react';
configure({ enforceActions: 'always' });
class Store {
my_number = 1;
addNumber() {
this.my_number += 1;
}
removeNumber() {
this.my_number -= 1;
}
}
decorate(Store, {
my_number: observable,
addNumber: action,
removeNumber: action
})
const my_store = new Store();
const Button = (props) => {
if (props.store.my_number === 1) {
return (
<div>
<button onClick={props.store.addNumber}>+</button>
</div>
)
} else if (props.store.my_number === 4) {
return (
<div>
<button onClick={props.store.removeNumber}>-</button>
</div>
)
} else {
return (
<div>
<button onClick={props.store.addNumber}>+</button>
<button onClick={props.store.removeNumber}>-</button>
</div>
)
}
}
const ObserverButton = observer(Button);
const DisplayNumber = (props) => {
return (
<h1>My number is: {props.store.my_number}</h1>
)
}
const ObserverDisplayNumber = observer(DisplayNumber);
export class SimpleMobxStore extends Component {
render() {
return (
<div>
<ObserverButton store={my_store} />
<ObserverDisplayNumber store={my_store} />
</div>
)
}
}
And my thoughts for developing it have been (I would also appreciate suggestions on how to improve my thoughts-flow if it's bad):
I want a text on the screen that shows a number between 1 and 4. Above this text I want to have a button that allows me to increase or decrease this number by adding or substracting a unit each time. I want this variable (the current number) to be stored in a separate store. That store will include:
My number
A method for increasing the number
A method for decreasing the number
In addition I will create two components: a button component that renders my button depending on the current number, and a display component.
My observable will be the number in the store, whereas the two methods will have to be decorated as actions, since they are changing the observed variable.
My button and display components will be observers, since they must be re-rendered once the number changes.
With this simple reasoning and code I was expecting it to function, but instead I'm getting a:
Error: [mobx] Since strict-mode is enabled, changing observed observable values outside actions is not allowed. Please wrap the code in an action if this change is intended. Tried to modify: Store#4.my_number
The log seems to be pointing to when I define const my_store = new Store();, but this is done in the tutorial and it works there.
Any idea on where this is failing and why?
Thank you
I think your action to the store is directly from render(). The tag to be precise. Try having a method outside the render and try changing the store state from there.

VeeValidate check for errors in entire scope

I am trying to render out a warning if there is an error within a specific scope. This is due to the form being across multiple tabs and hopefully making it easier for people to see what needs fixing.
The issue is, I have tried multiple methods but they are not working. This is the current method:
const TabInternals = Vue.component('TabInternals', {
props: {
title: String,
scope: String
},
render() {
return (
<div>
<i v-show={this.errors.any(`${this.scope}.*`)} class="fas fa-exclamation-circle"></i>{` ${this.title}`}
</div>
);
}
});
Can anyone see what I am doing wrong?
Thanks in advance
By default, each component gets its own instance of a vee validator (and therefore errors too). If you want to access errors from a different scope, you will need to use inject to pass the parent validator instance to the child components so that they share a validator instance:
export default {
inject:[ '$validator'],
// ...
};
ref - https://github.com/baianat/vee-validate/issues/1774

Vue component prop change does not trigger rerender

In my Vue 2 application I have a big object that is passed from the root component down several levels to some child components as a prop. When I change some property of the object the child components should update and rerender. In some cases they do, in other cases they don't. I need some help spotting why it does not work.
Here is a child component, which does not update:
<template>
<div class="upgradeBar">
{{level}}
<div
v-for="lvlNum in maxLevel + 1"
class="level"
v-bind:class="{reached: isLevelReached(lvlNum - 1)}"
></div>
<button
class="btnUpgrade"
#click="onLevelUp()"
v-if="!isLevelReached(maxLevel)"
>
+
</button>
</div>
</template>
<script lang="ts">
import {Component, Prop, Vue} from 'vue-property-decorator';
import Upgradable from "../../../models/Upgradable";
#Component()
export default class UpgradeBar extends Vue {
name: 'UpgradeBar';
#Prop() entity: Upgradable;
get level(): number {
return this.entity.level;
}
get maxLevel(): number {
return this.entity.MAX_LEVEL;
}
onLevelUp() {
this.entity.levelUp();
}
isLevelReached(level: number): Boolean {
return this.entity.level >= level;
}
}
</script>
The component is called like this:
<UpgradeBar :entity="entity" />
All the code works. When I click the btnUpgrade button entity.level is indeed changed, but I need to close and reopen the component to see the changes. Also the browser dev tool does not show the change instantly. I need to click on the component to refresh the values in the debugger.
EDIT:
The entity class looks basicly like this (excerpt):
class Entity {
name: string = 'some name';
level: number = 1;
}
I searched deeper and it seems to boils down to this: Some properties of the object are reactive (they have getters / setters created by vue) and some don't. entity.name has a setter, so changing it updates the component. entity.level does not. Here's the question: Why are they treated differently? Here is a log:
Can't tell for sure without seeing the code for entity.levelUp, but it seems like a reactivity issue, that may be solved by using Vue.$set inside that function.
You can confirm this being the case by adding this.$forceUpdate(); after this.entity.levelUp();
update
this._level = this._level + 1;
can be changed to
Vue.$set(this, _level, this._level + 1);
You will need to import Vue in that component/file to access the $set function
You don't show (or I can't find) the code that changes the object, but are you using $set() or Vue.set() instead of simply changing the object's properties directly? Changing a property directly generally doesn't work because of reactivity limitations
Edited to add:
I see now. I think you want something like:
this.$set(this, '_level', this._level + 1);

Accessing redux store via child component in react native

I am using Redux with React Native and I'm having problem in accessing a specific index of the redux store array with the key.
Here's a rough idea of what I'm trying to do. I have a ParentComponent that accesses the reduxStore for a state data and data contains an array of id,title in it.
class ParentComponent extends Component {
dataArray() {
return Object.keys(this.props.data).map(key => this.props.data[key])
}
/*
Consider the dataArray has elements which are an array of `id` & `title`
*/
render() {
return (
<ScrollView style={styles.fragmentContainer}>
{this.dataArray().map((element) => {
return
<ChildComponent
key={element.id}
id={element.id} />
})}
</ScrollView>
)
}
}
Now I wanna access title of each element of the array via the ChildComponent. This is what I'm trying to do to access it:
class ChildComponent extends Component {
render(
return (
<View>
<Text>{this.props.data[this.props.id].title}</Text>
</View>
)
)
}
Obviously the above doesn't work. It's just an idea of what I'm trying to do. I am unable to access the redux store data using a given key. I tried to find out how one can access it but I found no solution. Is my method of accessing the state for the ChildComponent wrong. Can anyone refer me to how I can achieve this?
NOTE: I specifically want to access the state via the ChildComponent. If that's not how it's supposed to work like, please refer me to some other way or some documentation that explains how this is supposed to be done.
If your child component is a class component, you can import connect, set up your reducer in mapStateToProps and bind them in your export.
You can also send the reducer prop value as props from parent to child component. Something like this <Child value={this.props.myData} />. Now in your child component you can refer it as this.props.value. If you use this approach, it doesn't matter if your child is a class component or not. Don't forget to also instance value in your child component creation.
Hope it helps.
First of all I don't see you passing a data prop from your parent to child. Thus you won't get this.props.data in your child component.
I don't understand why would you want to extract title that way when you can pass it as a prop just like you passed id.
If you specifically want to access redux store, then you use connect from react-redux.
Here's a link to see how to do that
http://redux.js.org/docs/basics/UsageWithReact.html

What is the name of my Vue component?

I'm trying to use Vue for a little project.
I started with only client code. So when I did
const mv = new Vue({...});
I was able to access to the component from outside with a mv.
Now I'm using Vue.cli, so I define my component inside a mv.vue and then I have
export default {
data () {}
}
Here, how can I get the nme of my component?
Thanks for any reply or hint :)
You can get the name of the component , you can do this
this.$vnode.tag
If you want the parent component's name from its child do this
this.$parent.$vnode.tag
You can name your component like so:
export default {
name: 'mv',
data () {
return {}
}
}
But to access it you'd need to use the name that you import it with. For example:
import theVariableIWantItToBe from './mv.vue'
console.log(theVariableIWantItToBe)
To expand on the very good #vamsi-krishna answer, and update it, I've discovered that Vue now often puts a prefix on the $vnode.tag along the lines of vue-component-3-YourComponentName.
You can fix this by using the following code. And perhaps, just in case of a missing tag, fall back to the ID of the root element in the component.
Occasionally, Vue doesn't pass back a component at all in its errorHandler and warnHandler global events. So I've handled that scenario first.
if (!vm){
return '[unknown]'
}
if (vm.$vnode.tag) {
const res = vm.$vnode.tag
return res.replace(/vue-component-\d+-/i, '')
}
if (vm.$el.id) {
return vm.$el.id
}