I've been searching for a couple of hours now, but just can't seem to find the answer. See my code below. I'm requesting some metro-information to be used on an info-screen.
I'm getting the information, seeing as console.log works. However I'm having difficulty using this resulting oject. I want to use the data received, so that I can display when the next train arives. To this purpose I try to setState with the result, so that I can access the data-elements further down. However, now I'm stuck at setState giving me problems. I feel that I need to bind the function, but this.main = this.main.bind(this) doesn't work.
import React from "react";
import { GraphQLClient } from "graphql-request";
class Rutetider extends React.Component {
constructor(props) {
super(props);
this.state = {
stoppestedet: "rutetider lastes ned"
};
async function main() {
const endpoint = "https://api.entur.org/journeyplanner/2.0/index/graphql";
const graphQLClient = new GraphQLClient(endpoint, {
headers: {
ET: "lossfelt-tavle"
}
});
const query = `
{
stopPlace(id: "NSR:StopPlace:58249") {
id
name
estimatedCalls(timeRange: 3600, numberOfDepartures: 20) {
realtime
aimedArrivalTime
aimedDepartureTime
expectedArrivalTime
expectedDepartureTime
actualArrivalTime
actualDepartureTime
cancellation
notices {
text
}
situations {
summary {
value
}
}
date
forBoarding
forAlighting
destinationDisplay {
frontText
}
quay {
id
}
serviceJourney {
journeyPattern {
line {
id
name
transportMode
}
}
}
}
}
}
`;
const data = await graphQLClient.request(query);
console.log(data);
this.setState({ stoppestedet: data.stopPlace.name });
}
main().catch(error => console.error(error));
}
render() {
return (
<div>
rutetider
<div className="grid-container2">
<div>Mot byen</div>
<div>fra byen</div>
<div>{this.state.stoppestedet}</div>
</div>
</div>
);
}
}
export default Rutetider;
"probably easier" is to use integrated solution (apollo) than minimal, low level library. In most cases (as project grows), with more components fetching data managing separate GraphQLClient for all of them won't be an optimal solution. Apollo gives you centralised "fetching point", cache .. and many more.
Syntax error comes from function - in class it's enough to write async main()
https://codesandbox.io/s/l25r2kol7q
It probably would be better to save entire data in state and extract needed parts later (at render) and use this object as 'data-ready flag' (as I did for place - 'stoppestedet') - initally undefined (in constructor) for initial render (conditional rendering, some <Loading /> component):
render() {
if (!this.state.stoppestedet) return "rutetider lastes ned";
return (
<div>
rutetider
<div className="grid-container2">
<div>Mot byen</div>
<div>fra byen</div>
<div>{this.renderFetchedDataTable()}</div>
</div>
Related
I am trying to add Application Insights in my ReactJS Application. I changed the JS code that is provided on the GitHub Demo to TypeScript.. now I have
class TelemetryProvider extends Component<any, any> {
state = {
initialized: false
};
componentDidMount() {
const { history } = this.props;
const { initialized } = this.state;
const AppInsightsInstrumentationKey = this.props.instrumentationKey;
if (!Boolean(initialized) && Boolean(AppInsightsInstrumentationKey) && Boolean(history)) {
ai.initialize(AppInsightsInstrumentationKey, history);
this.setState({ initialized: true });
}
this.props.after();
}
render() {
const { children } = this.props;
return (
<Fragment>
{children}
</Fragment>
);
}
}
export default withRouter(withAITracking(ai.reactPlugin, TelemetryProvider));
But when I try to import the same component <TelemetryProvider instrumentationKey="INSTRUMENTATION_KEY" after={() => { appInsights = getAppInsights() }}></Telemetry> I get an error Severity Code Description Project File Line Suppression State
(TS) JSX element type 'TelemetryProvider' does not have any construct or call signatures.
I attempted to simply // #ts-ignore, that did not work. How do I go about solving this?
Given the example above, I hit the same issue. I added the following:
let appInsights:any = getAppInsights();
<TelemetryProvider instrumentationKey={yourkeyher} after={() => { appInsights = getAppInsights() }}>after={() => { appInsights = getAppInsights() }}>
Which seem to solve the issue for me, I am now seeing results in Application Insights as expected.
I guess if you want to have the triggers etc on a different Page/Component you may wish to wrap it in your own useHook or just add something like this to the component.
let appInsights:any;
useEffect(() => {
appInsights = getAppInsights();
}, [getAppInsights])
function trackEvent() {
appInsights.trackEvent({ name: 'React - Home Page some event' });
}
Not the best answer, but it's moved me forward. Would be nice to see a simple hooks version in typescript.
Really hope it helps someone or if they have a cleaner answer.
Recently I contemplated the idea of having central state management in my React apps without using Redux or Mobx, instead opting to create something similar to the application class in Android. In any event, I implemented something similar to this:
Create a store folder and a file called store.js in it whose contents are:
// State
let state = {
users: {},
value: 0
};
// Stores references to component functions
let triggers = [];
// Subscription Methods
export const subscribe = trigger => {
triggers.push(trigger);
trigger();
}
export const unsubscribe = trigger => {
let pos = -1;
for (let i in triggers) {
if (triggers[i]===trigger) {
pos = i;
break;
}
}
if (pos!==-1) {
triggers.splice(pos, 1);
}
}
// Trigger Methods
let triggerAll = () => {
for (let trigger of triggers) {
trigger();
}
}
// State Interaction Methods
export const setUser = (name, description) => {
state.users[name] = description;
triggerAll();
}
export const removeUser = name => {
if (name in state.users) {
delete state.users[name];
}
triggerAll();
}
export const getAllUsers = () => {
return state.users;
}
export const getUser = name => {
if (!(name in state.users)) {
return null;
}
return state.users[name];
}
export const getValue = () => {
return state.value;
}
export const setValue = value => {
state.value = value;
triggerAll();
}
And connecting to this store in the following manner:
// External Modules
import React, { Component } from 'react';
import {Box, Text, Heading} from 'grommet';
// Store
import {subscribe, unsubscribe, getAllUsers} from '../../store/store';
class Users extends Component {
state = {
users: []
}
componentDidMount() {
subscribe(this.trigger); // push the trigger when the component mounts
}
componentWillUnmount() {
unsubscribe(this.trigger); // remove the trigger when the component is about to unmount
}
// function that gets triggered whenever state in store.js changes
trigger = () => {
let Users = getAllUsers();
let users = [];
for (let user in Users) {
users.push({
name: user,
description: Users[user]
});
}
this.setState({users});
}
render() {
return <Box align="center">
{this.state.users.map(user => {
return <Box
style={{cursor: "pointer"}}
width="500px"
background={{color: "#EBE7F3"}}
key={user.name}
round
pad="medium"
margin="medium"
onClick={() => this.props.history.push("/users/" + user.name)}>
<Heading margin={{top: "xsmall", left: "xsmall", right: "xsmall", bottom: "xsmall"}}>{user.name}</Heading>
<Text>{user.description}</Text>
</Box>
})}
</Box>;
}
}
export default Users;
Note. I've tested this pattern on a website and it works. Check it out here. And I apologize I am trying to keep the question concise for stackoverflow, I've provided a more detailed explanation of the pattern's implementation here
But anyway, my main question, what could be the possible reasons not to use this, since I assume if it was this simple, people wouldn't be using Redux or Mobx. Thanks in advance.
That's what Redux and MobX basically do, you are wrong in thinking that at their core concept they are much different. Their size and complexity came as a result of their effort to neutralize bugs and adapt to a vast variety of application cases. That's it. Although they might be approaching the task from different angles, but the central concept is just that. Maybe you should familiarize your self with what they actually do underneath.
Btw, you do not need to store redundant state in your component, if all you need is to trigger the update. You can just call forceUpdate() directly:
// function that gets triggered whenever state in store.js changes
trigger = () => {
this.forceUpdate();
}
That's similar to what Redux and MobX bindings for react do under the hood.
I'm working on a project where a prospect needs to be sent an email about a property they are interested in. There is a top level component that fetches the property information and prospect's contact info from the database and passes to its children. There are two components that share the same process of formatting the information, and then call an email function that sends off an email. A sample of one component looks like this:
import sendEmail from 'actions/sendEmail'
class PropertyDetail extends React.Componet {
state = {
unit: undefined,
prospect: undefined,
};
componentDidMount = () => {
this.setState({
unit: this.props.unit,
prospect: this.props.prospect,
});
};
sendEmail = ({ id, address, prospect }) => {
// quite a bit more gets formatted and packaged up into this payload
const payload = {
id,
address,
prospectEmail: prospect.email,
};
emailFunction(payload);
};
handleEmail = () => {
sendEmail(this.state);
};
render() {
return (
<div>
<h1>{this.state.unit.address}</h1>
<p>Send prospect an email about this property</p>
<button onClick={this.handleEmail}>Send Email</button>
</div>
);
}
}
and the other component looks like this
class UpdateShowing extends React.Component {
state = {
unit: undefined,
prospect: undefined,
showingTime: undefined,
};
componentDidMount = () => {
this.setState({
unit: this.props.unit,
propsect: this.props.prospect,
showingTime: this.props.showingTime,
});
};
sendEmail = ({ id, address, prospectEmail }) => {
// quite a bit more gets formatted and packaged up into this payload
const payload = {
id,
address,
prospectEmail,
};
emailFunction(payload);
};
handleUpdate = newTime => {
// get the new date for the showing ...
this.setState({
showingTime: newTime,
});
// call a function to update the new showing in the DB
updateShowingInDB(newTime);
sendEmail(this.state);
};
render() {
return (
<div>
<p>Modify the showing time</p>
<DatePickerComponent />
<button onClick={this.handleUpdate}>Update Showing</button>
</div>
);
}
}
So I see some shared functionality that I'd love to not have to repeat in each component. I'm still learning (working my first job), and why not use this as an opportunity to grow my skills? So I want to get better at the HOC/Render props pattern, but I'm not sure if this is the place to use one.
Should I create a component with a render prop (I'd rather use this pattern instead of a HOC)? I'm not even sure what that would look like, I've read the blogs and watched the talks, ala
<MouseMove render={(x, y) => <SomeComponent x={x} y={y} />} />
But would this pattern be applicable to my case, or would I be better off defining some lib function that handles formatting that payload for the email and then importing that function into the various components that need it?
Thanks!
I think a provider or a component using render props with branching is a better fit for you here
see this doc: https://lucasmreis.github.io/blog/simple-react-patterns/#render-props
I have a query which gets me a list of notes and a subscription which listens and inserts new notes by altering the query. However the problem is the first note doesn't get added.
So let me add more detail, initially the query response with an object which contains an attribute called notes which is an array of 0 length, if we try and add a note the attribute gets removed. The note is created so if I refresh my application the query will return the note then If I try and add a note again the note gets added to the array in the query object.
Here is my notes container where I query for notes and create a new property to subscribe to more notes.
export const NotesDataContainer = component => graphql(NotesQuery,{
name: 'notes',
props: props => {
console.log(props); // props.notes.notes is undefined on first note added when none exists.
return {
...props,
subscribeToNewNotes: () => {
return props.notes.subscribeToMore({
document: NotesAddedSubscription,
updateQuery: (prevRes, { subscriptionData }) => {
if (!subscriptionData.data.noteAdded) return prevRes;
return update(prevRes, {
notes: { $unshift: [subscriptionData.data.noteAdded] }
});
},
})
}
}
}
})(component);
Any help would be great, thanks.
EDIT:
export const NotesQuery = gql`
query NotesQuery {
notes {
_id
title
desc
shared
favourited
}
}
`;
export const NotesAddedSubscription = gql`
subscription onNoteAdded {
noteAdded {
_id
title
desc
}
}
`;
Another EDIT
class NotesPageUI extends Component {
constructor(props) {
super(props);
this.newNotesSubscription = null;
}
componentWillMount() {
if (!this.newNotesSubscription) {
this.newNotesSubscription = this.props.subscribeToNewNotes();
}
}
render() {
return (
<div>
<NoteCreation onEnterRequest={this.props.createNote} />
<NotesList
notes={ this.props.notes.notes }
deleteNoteRequest={ id => this.props.deleteNote(id) }
favouriteNoteRequest={ this.props.favouriteNote }
/>
</div>
)
}
}
Another edit:
https://github.com/jakelacey2012/react-apollo-subscription-problem
YAY got it to work, simply the new data sent down the wire needs to be the same shape as the original query.
e.g.
NotesQuery had this shape...
query NotesQuery {
notes {
_id
title
desc
shared
favourited
}
}
yet the data coming down the wire on the subscription had this shape.
subscription onNoteAdded {
noteAdded {
_id
title
desc
}
}
notice shared & favourited are missing from the query on the subscription. If we added them it would now work.
This is the problem, react-apollo internally detects a difference and then doesn't add the data I guess It would be useful if there was a little more feed back.
I'm going to try and work with the react-apollo guys to see if we can put something like that in place.
https://github.com/apollographql/react-apollo/issues/649
I've been tasked with creating a clone of the popular game show Family Feud. I am having trouble finding a good way to take a list of "rounds" each with a question and an array of answers (something like)
const Data = {
round_1: {
question: "A question",
answers: ["one answer", "second one", ...etc]
},
round_2: {
question: ...,
answers: [...]
},
...etc
}
and then rendering the relevant round info into a component such that the player can go forward and back rounds and only have the relevant rounds info mapped to the component. Something like:
// Gameboard.js
import React from 'react'
import {Data} from './data'
export default class extends React.Component {
componentWillMount() {
const round = 1
const ansArray = []
const roundQuestion = Data.round_`${round}`.question
const roundAnswers = Data.round_`${round}`.answers.map(ans=>{
ansArray.push(ans)
})
this.setState({
question: roundQuestion,
answers: roundAnswers
})
}
handlePrevRoundClick() {
this.setState({set to prev round})
}
handleNextRoundClick() {
this.setState....
}
render() {
return (
<div>
<RoundInfo roundNum={this.state.round}/>
<Round answers={this.state.answers}/>
</div>
)
}
}
Then obviously the components RoundInfo and Round are merely passed their info via props. I have left them off the example because I think that the logic i'm after only applies to this main "Gameboard" component. I would be more than happy to expand if necessary. I also realize that there may be typos and missing bits here. I'd be happy to jsfiddle it if anyone needs a bit more to go on. I do have a working prototype which i have manually created each rounds' own component but this is decidedly not the React Way...
In summary: I am asking for methods to use a variable to "walk" back and forth between a list of objects, only having to map the current rounds' info to the board.
Thanks!
It's like you already did most of the job. To complete what you did:
// Gameboard.js
export default class extends React.Component {
componentWillMount() {
// init round to 0
this.setState({
round:0
});
}
handlePrevRoundClick = () => {
const round = this.state.round - 1;
this.setState({round}); // update round
}
handleNextRoundClick= () => {
const round = this.state.round + 1;
this.setState({round}); // update round
}
render() {
const curRoundData = Data['round_'+this.state.round];
return (
<div>
<RoundInfo roundNum={this.state.round}/>
<Round answers={curRoundData.answers}/>
<div onClick={this.handlePrevRoundClick}>Go Prev</div>
<div onClick={this.handleNextRoundClick}>Go Next</div>
</div>
)
}
}