I have a component called RpContainer which will receive data by its props called 'completeReport' like that :
export default {
props: ['completeReport'],
created() {
}
}
This component is used in my main Vue called Report like that :
<RpContainer v-if="displayReport == true" :completeReport="indicatorList"></RpContainer>
In my main Vue, I'm doing some API calls to get some data, with those data, I built an array (indicatorList). And what I want is to link this array once it's completly done (there is a delay between my call and the answer) with my props 'completeReport'.
But the problem is that when my component RpContainer is created, my array is still empty. So how could I link it properly to have an update in my component when my array is succesfully build.
I tried to put a watcher my props but it's not working..
Can you help me ? Thanks
Related
I have a functional component. Basically, the page consists of a form - where I need to populate some existing data into the form and let the user update it.
I'm using a hook that I wrote to handle the forms. What it does is this
const [about, aboutInput] = useInput({
type: 'textarea',
name: 'About you',
placeholder: 'A few words about you',
initialValue: data && data.currentUser.about,
})
about is the value and aboutInput is the actual input element itself. I can pass an initialValue to it too.
At the beginning of the component I'm fetching the data like so:
const { data, loading, error } = useQuery(GET_CURRENT_USER_QUERY)
This only is executed on the client side and on the server side data is undefined.
Hence this code only works when I navigate to the page through a Link component from another client-side page.
It doesn't work for:
When I go the URL directly
When I navigate to this page from another SSR page(which uses getInitailProps)
I don't want to use lifecycle methods/class component(since I'm using hooks, and want to keep using the functional component.
Is there a nice way to achieve this in Next JS and keep using functional component?
You can fetch client-side only data using the useEffect Hook.
Import it first from react
import { useEffect } from 'react';
Usage in the component looks like follows
useEffect(() => {
return () => {
// clean-up functions
}
}, []);
The first argument Is a function and you can make your API calls inside this.
The second argument to the useEffect will determine when the useEffect should be triggered. If you pass an empty array [ ], then the useEffect will only be fired when the component Mounts. If you want the useEffect to fire if any props change then pass such props as a dependency to the array.
If you want GET_CURRENT_USER_QUERY from the query string you can pass the argument from the getInitailProps and read this as props in the useEffect array dependency.
I see you mentioned getInitailProps. So you probably know what it does and its caveats. If you want the data to be defined on server side, why not use getInitailProps on the page which the form is in. That way you can retrieve the data from props and pass it to your form component. It should work either directly visiting the url or visiting from your other pages.
I am building a blog in react where I get the data from a JSON-API and render it dynamically. I call setState in my app.js to get the data from JSON via axios and route with match.params to the post pages(childs). This works fine, BUT if I call the child (URL: ../blog/post1) in a seperate window it doesnt get rendered. It works if I hardcode the state.
So, I see the issue, what would be the best way to fix it?
Parent:
class App extends Component {
constructor(props) {
super();
this.state = {
posts: []
}
getPosts() {
axios.get('APIURL')
.then(res => {
let data = res.data.posts;
this.setState({
posts: data
})
})
}
componentDidMount = () => this.getPosts()
Child:
UPDATE - Found the error:
The component threw an error, because the props were empty. This was because the axios getData took some time.
Solution: I added a ternary operator to check if axios is done and displayed a css-spinner while loading. So no error in component.
Dynamic Routing works like a charme.
You can do this with binding. You would need to write a function like
setPosts(posts) {
this.setState({posts});
}
in the parent, and then in the parent, bind it in the constructor, like so.
this.setPosts = this.setPosts.bind(this);
When you do this, you're attaching setPosts to the scope of the parent, so that all mentions of this in the function refer to the parent's this instead of the child's this. Then all you need to do is pass the function down to the child in the render
<Child setPosts={this.setPosts} />
and access that method in the child with
this.props.setPosts( /*post array goes here */ );
This can apply to your getPosts method as well, binding it to the parent class and passing it down to the Child.
When you hit the /blog/post1 url, it renders only that page. The App component is not loaded. If you navigate from the App page, all the loading has been done and you have the post data in the state of the child component.
Please refer to this SO question. This should answer your question.
Quick React question for Components specifically. I am using two lifecycle methods:
componentDidMount() -for retrieving data when component is first rendered
componentWillReceiveProps(nextProps) - for updating data when some parameters change
This works great. However, when I refresh the page with the component, both lifecycle methods are executed when one will suffice. The data on page is still correct, however it seems a bit inefficient. How can I combine these to lifecycles if possible?
Below example will call fetchTest() twice when page is refreshed. If I remove componentDidMount, then the data will not initially load if user refreshes the page.
Any ideas on how to have fetchTest() called once no matter how the user gets to the component?
componentDidMount() {
fetchTest(this.props.params.id);
// for initial component render
}
componentWillReceiveProps(nextProps) {
fetchTest(nextProps.params.id);
// for when (params.id) is changed. data within component is updated
}
You probably want to do
componentWillReceiveProps(nextProps) {
if (nextProps.params.id !== this.props.params.id) {
fetchTest(nextProps.params.id);
}
}
Edit
Actually, I'm fairly certain that this is because my action creator doesn't return an action and the first property of an action is supposed to be a type and that is therefore undefined...
I have a React/Redux SPA that I want to register page views on with a custom analytics engine (ie, not Google Analytics). I'm trying to register page views.
So I have attempted to do this by setting lifecycle hooks in React to fire a Redux action:
class ConfirmationPage extends Component {
...
componentWillMount() {
this.props.registerPageVisited('confirmation');
}
}
However, I receive a Cannot read property 'type' of undefined error presumably because I am modifying the state via the props. Looking at the stack trace, it brings me to that hook. However, I've tried other hooks such as componentDidMount and even componentWillUnMount and I get the same error.
For context, my action creator is this:
export function registerPageVisited(page) {
DB.child('visits')
.child(store.getState().visit)
.update({ [page]: true });
}
where the DB is a firebase reference.
So, how should I keep track of page views?
Could you provide code where you instantiate ConfirmationPage component?
In the componentWillMount function what you are actually doing is calling registerPageVisited function which should be passed as props which should look something like: <ComponentPage registerPageVisited={registerPageVisited} />
As you are suspecting, since your registerPageVisited does not return action object it is not action creator. If you will make it an action creator, then you should use mapDispatchToProps function to use it like any other props as sen in your example.
An alternate to this is have a different object listen to notifications from the store and update the DB based on the changes it's seeing. That will isolate any metrics collection from your view hierarchy.
You'd have to replicate some logic that determines which React components are getting shown into that object, but you could pull that into a separate class and use it in both places.
If you're using react-router, there might be hooks that fire when the active route changes. However, I haven't investigated that.
My TranslationDetail component is passed an id upon opening, and based on this an external api call is triggered in the class constructor, receiving data to the state, and this data being displayed on TranslationDetail.
//Routing:
<Route path="/translation/:id" component={TranslationDetail}/>
//Class:
class TranslationDetail extends Component {
constructor(props){
super(props);
this.props.fetchTrans(this.props.params.id);
}
This all works fine if I enter the url manually. In case I'd like to use react-router e.g. for displaying the next item like below the url does change, but the api call is not triggered, and the data will remain the same.
<button
type="button"
onClick={() =>
browserHistory.push(`/translation/${Number(this.props.params.id)+1}`)}>
Next
</button>
Please bear in mind that I'm a total beginner. The reason why this is happening is I believe that the constructor is run only once, thus no further api call is triggered.
How can I solve this?
Do I need to listed to props and call a function on change? If yes, how?
Constructor is not a right place to make API calls.
You need to use lifecycle events:
componentDidMount to run the initial fetch.
componentDidUpdate to make the subsequent calls.
Make sure to compare the props with the previous props in componentDidUpdate to avoid fetching if the specific prop you care about hasn't changed.
class TranslationDetail extends Component {
componentDidMount() {
this.fetchTrans();
}
componentDidUpdate(prevProps) {
if (prevProps.params.id !== this.props.params.id) {
this.fetchTrans();
}
}
fetchTrans() {
this.props.fetchTrans(this.props.params.id);
}
}
From React 16.3 and onwards componentWillMount, componentWillUpdate and componentWillReceiveProps are deprecated.
You can use static getDerivedStateFromProps and return a new state based on changes on props.
You don't have access to your this objects like props, so you cannot compare nextProps with your current props by nextProps.sth !== this.props.sth. You can compare you prevState value with nextProps and return new value of state.
Make sue you add UNSAFE_ to your current componentWillMount and the other deprecated lifecyle methods for now.
Use componentWillMount to get the data and set the state.
Then use componentWillReceiveProps for capturing update on the props.
You can check the Component Specs and Lifecycle.
I would use the render method. If the data is not loaded I would render a loader spinner and throw the action that fetch de data. For that i usually use the stores. Once the store has de data from the api, mark the data as loaded, throw an event and let the component get the data from the store, replacing the loader spinner with your data representation.