Callback onLoad for React-Apollo 2.1 - reactjs

I want to know what's the best way to handle setting a parent's state when the Apollo <Query> component finishes loading? I have an id that I sometimes have to query for. I wonder what's the best way to handle this case?
Currently I have it where the child component will listen for prop changes and if I notice that the prop for the data I'm looking for changes I'll call a function to update the state.
Is there a better way to handle this without needing the child component to listen to updates?
This is a pseudo code of what I'm currently doing
import * as React from 'react';
import { Query } from 'react-apollo';
class FolderFetcher extends React.Component<Props, { id: ?string}> {
constructor(props) {
super(props);
this.state = {
id: props.id
}
}
setId = (id) => {
this.setState({ id });
};
render() {
const { id } = this.props;
return (
<Query skip={...} query={...}>
((data) => {
<ChildComponent id={id} newId={data.id} setId={this.setId} />
})
</Query>
);
}
}
class ChildComponent extends React.Component<Props> {
componentDidUpdate(prevProps) {
if (prevProps.newId !== this.props.newId &&
this.props.id !== this.props.newId) {
this.props.setId(this.props.newId);
}
}
render() {
...
}
}

you can export child as wrapped with HoC from apollo
import { graphql } from 'react-apollo'
class ChildComponent extends React.Component {
// inside props you have now handy `this.props.data` where you can check for `this.props.data.loading == true | false`, initially it's true, so when you will assert for false you have check if the loading was finished.
}
export default graphql(Query)(ChildComponent)
Another option would be to get client manually, and run client.query() which will return promise and you can chain for the then() method.

Related

The data that comes from an API end point is undefined in the child component in React

Good day to all!
I have this situation: I use Apollo client to get data from a GraphQL API endpoint in the parent class component in React. I pass this data to the child class component. The first time everything works fine but after a page refresh the data in the child component becomes undefined and the app crashes.
Here is the representation of the situation:
The ParentComponent
import React, { Component } from 'react'
import { gql } from "apollo-boost";
import {graphql} from 'react-apollo';
import ChildComponent from './ChildComponent'
const getProducts = gql`
{
category {
products {
id
name
gallery
}
}
}
`
class ParentComponent extends Component {
constructor(props) {
super(props)
this.state = {
products: []
}
}
componentDidMount() {
setTimeout(() => {
this.setState({
products: [...this.props.data.category.products]
})
}, 1000)
}
render () {
let products = this.state.products;
return (
<div><ChildComponent theProducts = {products}/></div>
)
}
}
export default graphql(getProducts)(ParentComponent);
The ChildComponent
import React, { Component } from 'react'
class ChildComponent extends Component {
constructor(props) {
super(props)
this.state = {
products: this.props.theProducts
}
}
render () {
let item = this.state.products.find(each => each.id === id);
return (
<div>
<ul>
<li>{item.name}</li>
<li><img src= {item.gallery[0]} alt="product"></img></li>
</ul>
</div>
)
}
}
export default ChildComponent;
So, when the app starts everything seems to work fine. But if I refresh the page it throws an error and says that name is undefined, gallery is undefined. It is clear that the data is not coming through to the ChildComponent. Is there a way to make sure that the data comes in at any time?
Thank you in advance.
You use theProducts in the ChildComponent but you pass theProduct from ParentComponent . And state product also has the same error. Just update to theProducts and product

React native context api not updated

I'm using RN NetInfo to check if user connected to internet using component <NetworkProvider /> and I want to pass this components stats to all screens and components in my app.
The problem is context api works good when I use it inside render function but when I use inside componentDidMount or componentWillMount the state not changed. Return initial value of isConnected state.
Please read comment in code
so this my code
NetworkProvider.js
import React,{PureComponent} from 'react';
import NetInfo from '#react-native-community/netinfo';
export const NetworkContext = React.createContext({ isConnected: true });
export class NetworkProvider extends React.PureComponent {
state = {
isConnected: true // initial value
};
componentDidMount() {
NetInfo.isConnected.addEventListener('connectionChange', this.handleConnectivityChange);
}
componentWillUnmount() {
NetInfo.isConnected.removeEventListener('connectionChange', this.handleConnectivityChange);
}
handleConnectivityChange = isConnected => this.setState({ isConnected });
render() {
return (
<NetworkContext.Provider value={this.state}>
{this.props.children}
</NetworkContext.Provider>
);
}
}
this index.js
...
import { NetworkContext } from '../components/NetworkProvider';
export default class index extends Component {
static navigationOptions = {};
static contextType = NetworkContext;
constructor(props) {
super(props);
this.state = {
...
};
}
componentDidMount() {
// return object state but with inital value {isConnected :true}
console.log(this.context);
//this.fetchData(this.state.page);
}
render() {
// here when I use this.context return object {isConnected:true/false} depends on internet connection status on device
return(
<FlatList
...
/>
)
}
}
...

Attaching / Detaching Listeners in React

I have a component which, depending on its prop (listId) listens to a different document in a Firestore database.
However, when I update the component to use a new listId, it still uses the previous listener.
What's the correct way to detach the old listener and start a new one when the component receives new props?
Some code:
import React from 'react';
import PropTypes from 'prop-types';
import { db } from '../api/firebase';
class TodoList extends React.Component {
state = {
todos: [],
};
componentWillMount() {
const { listId } = this.props;
db.collection(`lists/${listId}/todos`).onSnapshot((doc) => {
const todos = [];
doc.forEach((t) => {
todos.push(t.data());
});
this.setState({ todos });
});
};
render() {
const { todos } = this.state;
return (
{todos.map(t => <li>{t.title}</li>)}
);
}
}
TodoList.propTypes = {
listId: PropTypes.object.isRequired,
};
export default TodoList;
I've tried using componentWillUnmount() but the component never actually unmounts, it just receives new props from the parent.
I suspect that I need something like getDerivedStateFromProps(), but I'm not sure how to handle attaching / detaching the listener correctly.
Passing a key prop to the TodoList lets the component behave as it should.

Unable to read a state from a reactjs component after it is set

I have set a simple example here: https://www.webpackbin.com/bins/-KhBJPs2jLmpQcCTSRBk
This is my class in the question:
import React, { Component, PropTypes } from 'react';
import { Redirect } from 'react-router-dom';
class RedirectMessage extends Component {
componentWillMount () {
const { timeToRedirect } = this.props;
const time = timeToRedirect || 5000;
console.log(`timer is set to ${time}`)
this.timer = setInterval(this.setoffRedirect.bind(this), time)
}
componentWillUnmount () {
clearInterval(this.timer);
}
setoffRedirect () {
console.log('setoffRedirect');
clearInterval(this.timer);
this.setState({startRedirect: true})
}
getDestination() {
const { destination } = this.props;
return destination || '/';
}
render() {
const {
message,
startRedirect
} = this.props;
console.log(`setoffRedirect >>> ${startRedirect}`);
if (startRedirect) {
return (
<Redirect to={this.getDestination()} />
)
}
return (
<div >
{message}
</div>
);
}
}
export default RedirectMessage;
What I want to achieve is to display a message before I redirect to another url
Here are the logic:
set up a timer in componentWillMount
When the timer callback is called, it uses setState to set startRedirect to true
In render if startRedirect is true, a Redirect tag will be rendered can cause url redirect
However the problem is the startRedirect remains undefined even after the timer callback is called. What have I missed?
class SomeClass extends Component {
constructor(props){
super(props);
this.state = {startRedirect: false};
}
}
The state object is not defined when using classes (es6). In cases where it is needed, you need to define it in the constructor to make it accessible.
You are also targeting startRedirect from this.props, instead of this.state within render

Meteor React Komposer: Execute Component Constructor AFTER ready subscription

I have a React Component Post:
export class Post extends React.Component {
constructor(props) {
this.state = this.props;
}
...
}
, which I compose with
import { composeWithTracker } from 'react-komposer';
import { composeWithTracker } from 'react-komposer';
import Post from '../components/post.jsx';
function composer(props, onData) {
const subscription = Meteor.subscribe('post', props.postId);
if (subscription.ready()) {
const data = {
ready: true,
posts: Posts.findOne(props.postId).fetch()
}
onData(null, data);
} else {
onData(null, {ready: false});
}
}
export default composeWithTracker(composer)(Post);
. As given in the Post Component I want to put some properties in the state of the component, but the constructor will be executed before I get the data from the composer!
How do I wait until the data is send and then put my props into the state?
Isn't this what the React Kompose should do? BTW I am using Version 1.~ to get composeWithTracker.
You could use componentWillReceiveProps to get new properties and set as component state. This function will run whenever there are new properties passed to component:
export class Post extends React.Component {
// ...
componentWillReceiveProps(nextProps) {
this.setState({
...nextProps,
});
}
// ...
}

Resources