At work we are working with VueJS and we want to render one element with the contents from a 2D array. This needs 2 for loops to iterate through which isn't possible with a template. We found out we can do this with the render() function.
Our code (but simplified a bit) is:
export default {
name: 'od-table',
data() {
return {
testData: []
}
},
render() {
console.log("render function")
}
};
When we include this component in the project the render function doesn't get called. I can see the component has loaded because I can read the test-data. No logs or any other sign the render function has been called.
It could be it's an obvious mistake since we have never got this working before, but since this is something we have to use for this problem we would like to know what is wrong with the code.
Given it worked when you recreated your component file from scratch, the issue might have been that in your original file you had mistakenly left a section. That's what happened to me. If your component comes with its own <template>, it will be used for rendering and your render() function will never get called at all.
I still don't know what was wrong, but deleting the component and copying the old code in the new files seemed to solve the problem.
Any particular reason why you're using render? You might be looking for mounted.
Here is a complete list of lifecycle hooks for Vue.js 2.0. https://v2.vuejs.org/v2/api/#Options-Lifecycle-Hooks
Related
EDIT: Solved thanks to #Mythos, but I'm very grateful to anyone who put their time into helping me, I was stuck on this for hours. Thanks a lot!
I have a Vue.js project created using vue-cli 4 and Vue 2.
It seems like a v-for I'm using to render a list is getting data too late. This is how my component is set up:
import { readLocalStorage } from '../../public/localStorage.js'
export default {
name: 'lista',
components: {
codiceLista
},
data(){
let salvati = readLocalStorage()
return {
codici: salvati
}
}
}
I have a component (codiceLista) which is rendered using v-for and data from the data function, and I'm experiencing a very weird behaviour. Whenever I manually reload the page nothing renders, telling me that my v-for is trying to access data that is not defined. However, if I remove the : in front of the v-for, which causes an error, add it again, the server auto-reloads and I see the list, but if I manually reload, without touching the code nothing renders and I get the same error in the console. Note that I have other elements in the page apart from the list, but when the error in the console appears even those don't render, even if completely unrelated and aren't using nothing from the component's data function. Bear with me, as I'm a beginner in Vue.js and new to programming in general. Any help would be kindly appreciated.
Don't add a : in front of v-for. : is a shorthand for v-bind. You can't bind v-for.
Also, initialize all the data first, and then you can populate it in lifecycle hooks.
data() {
return {
codeici: [],
}
}
mounted() {
this.codeici = readLocalStorage();
}
see Vue Lifecyle Hooks
In times past, my colleagues and I would typically write React Testing Library (RTL) tests for the main parent components, which often have many nested child components. That testing made sense and worked well. Btw the child components in question are very much dedicated to that parent component and not of the reusable variety.
But now we're trying to write RTL tests for every single component. Today I was trying to build tests for an Alerts component, which is the parent of an Alert component and about 4 levels down from the top-level component. Here's some sample code in my test file:
function renderDom(component, store) {
return {
...render(<Provider store={store}>{component}</Provider>),
store,
};
}
let store = configureStore(_initialState);
const spy = jest.spyOn(store, 'dispatch');
const { queryByTestId, queryByText, debug } = renderDom(
<Alerts question={store.getState().pageBuilder.userForm.steps[0].tasks[0].questions[1]} />,
store
);
I then started writing the typical RTL code to get the Alerts component to do its thing. One of these was to click on a button which would trigger an ADD_ALERT action. I stepped through all of the code and the Redux reducer was apparently working correctly with a new alert, as I intended, yet back in the Alerts component, question.alerts remained null whereas in the production code it was definitely being updated properly with a new alert.
I spoke with a colleague and he said that for this type of test, I would need to artificially rerender the component like this:
rerender(<Provider store={store}><Alerts question={store.getState().pageBuilder.userForm.steps[0].tasks[0].questions[1]} /></Provider>);
I tried this and it appears to be a solution. I don't fully understand why I have to do this and thought I'd reach out to the community to see if there was a way I could avoid using rerender.
It's hard to be certain without seeing more of your code, but my typical approach with RTL is to take the fireEvent call that simulates clicking the button and wrap it in an act call. This should cause React to finish processing any events from your event, update states, rerender, etc.
Alternatively, if you know that a particular DOM change should occur as a result of firing the event, you can use waitFor. An example from the React Testing Library intro:
render(<Fetch url="/greeting" />)
fireEvent.click(screen.getByText('Load Greeting'))
await waitFor(() => screen.getByRole('alert'))
Is there anything wrong with passing a value into a custom component like this? I noticed that when I console.log inside someFunction it is called many times when the component is loaded. Can someone explain?
HTML
<custom-component [someInput]=“someFunction(‘someParameter’)></custom-component>
TS
someFunction(someParameter) {
return someValue
}
YES, You are right,
In angular you should not call functions in template.
Reason
The main goal of angular is, Rendering the DOM when detecting any changes. So If angular detect any changes/updates in your application, It will re-render the template. So when It re-render each time, The function you used in template(props) will be called.
Always follow the best practices
why-you-should-never-use-function-calls-in-angular-template-expressions
According to the react documentation: http://facebook.github.io/react/docs/component-specs.html#mounting-componentdidmount we are adviced use componentDidMount for AJAX call that should be issued when a component is brought into view.
However, when switching between to instances of the same component with different props, componentDidMount is only called for the first component. So what are we supposed to do in this situation?
Currently I have the following workaround: In componentDidMount i do my AJAX call and In componentDidUpdate I compare old and new props to check if I am on a new "instance", and if so I do my AJAX call. But that seems exactly like a workaround. So my question is: is this really the way to do it?
I am aware that I could wrap my component in different empty components to solve my problem. However, this is not possible because we are building a data driven application that uses configurable components and it makes sense to use the same component with different configurations - which is where I'm running into problems.
I am aware that we are actually talking about react elements and not instances as such - witch I guess is part of the problem. Probably I have different react elements utilizing the same instance.
I have made a tiny example to illustrate the react behavior, using plain react (just to make sure I wasn't tricked by react-router or redux and what else we are using the real app):
class Foo extends React.Component {
componentDidMount() {
console.log('componentDidMount ' + this.props.foo);
}
componentDidUpdate() {
console.log('componentDidUpdate ' + this.props.foo);
}
render() {
return <div>Route is {this.props.foo}</div>;
}
}
function navigated() {
ReactDOM.render(
<Foo foo={window.location.hash} />,
document.getElementById('app')
);
}
window.addEventListener('hashchange', navigated, false);
navigated();
Initially when I go to #/bar I get 'componentDidMount #/bar' and when I go to #/baz i get 'componentDidUpdate #/baz'.
I seems like this unanswered question is a specific case of the same issue: React does not know when i render the same component
You can add the key property with unique value for each of hashes:
ReactDOM.render(
<Component hash={hash} key={hash} />, domNode
);
This will update the component every time when the hash is really changed.
https://facebook.github.io/react/docs/multiple-components.html#dynamic-children
TL DR - your 'workaround' looks correct to me
When you initially render the component componentDidMount is called, when you change the hash prop componentDidUpdate is called. It is still the same component, it is just that a specific prop has changed value. In your case, you have logic (running an AJAX call when hash changes) that is specific to your application. React does not known that the hash prop is special, you make is special by adding the logic in componentDidMount. So I believe you have a good interpretation of the React docs and this way of achieving your goal is perfectly valid.
When I work with JS I tend to whip out a console for the browser and manipulate values on the fly.
I have a page where I use React to render some components and I had the idea that it would be great to be able to manipulate it's state from the console to debug a design quirk which is only visible if the component is in a corner-case state.
I ran into problem that I was unable to get hold of a reference to my component.
I figured there might be a list of active components currently being rendered somewhere, but I was not able to find one on the React global object or anywhere else.
Is there an exposed reference to the components being rendered?
I'm rendering the component like:
<script>React.render(React.createElement(Comp, domElem))</script>
I could store a reference to the result of React.createElement() but it seems to be an antipattern. Also I'm using the ReactJS.NET library to handle server-side rendering for me so the whole React.render line is generated and is hard to modify.
My other idea was to create a mixin that makes the component explicitly expose itself on mount, like:
var ActiveComponents = [];
var debugMixin = {
componentDidMount: function () {
var id = this.getDOMNode().id;
ActiveComponents[id] = {
id: id,
getState: () => { return this.state; },
setState: (state) => { this.setState(state); },
comp: this
};
}
};
Are there drawbacks for an approach like this? Is this the same antipattern mentioned above?
Although being much cleaner than entangling these test hooks in the component code directly, adding a mixin is still a modification, and I would like to avoid that if possible.
The questions I hope to get answers for are bolded.
A workaround for this is to assign your object to the window object:
window.myStateObject = myStateObject
and then you can inspect it in the console:
window.myStateObject
There is a ReactJS extension for Chrome that may meet your needs https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi
If that isn't good enough, React keeps track of all the mounted components in a private variable instancesByReactID. If you just want to access these for debugging, you could modify the React code and expose that variable as a global.