Version
4.1.1
Steps to reproduce
This component is querying a db via GraphQL thanks to Apollo. It is a GraphQL container.
It uses compose from react-apollo library in order to use multiple enhanchers at the same time, in a readable way.
Example component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { graphql, compose, withApollo } from 'react-apollo';
import { myQuery } from './graphqlQuery';
class MyComponent extends Component {
render() {
const { exampleProp } = this.props;
return null;
}
}
function mapStateToProps(state, ownProps) {
// I expect to see ownProps.exampleProp here but it is undefined
console.log(ownProps.exampleProp);
}
export default compose(
withApollo,
connect(mapStateToProps),
graphql(myQuery),
)(MyComponent);
Expected Behavior
ownProps should contain the props passed to the component as stated here
like:
Object = {
client: ApolloClient
exampleProp: "propcontent", // <-- this is going to be lost
history: Object
location: Object
match: Object
staticContext: undefined
__proto__: Object
}
Actual Behavior
instead ownProps contains only this:
Object = {
client: ApolloClient
history: Object
location: Object
match: Object
staticContext: undefined
__proto__: Object
}
All the props that the component should have had from parents and from GraphQL response are missing, including, in this case, exampleProp.
Related
I am using Angular and have stored a json file with data in it locally.
I am accessing the json file by importing it into my component through:
import * as data from '../../data/countries.json';
In my tsconfig.json file, I have set the following:
"resolveJsonModule": true
I am running into issues when using the data set.
The following works:
console.log(data[0].Country); and this returns me the name of the first country in the list, printing it to the chrome console.
However, when I attempt to use this data within the component.ts code, I get the following errors:
Code:
for (let i = 0; i < 50; i++) {
let name :string = data[i].Country;
this.addCoordinates(name, data[i].latitude, data[i].longitude);
}
Error:
core.js:6210 ERROR TypeError: Cannot read property 'Country' of undefined
at GlobeComponent.changeCountry (globe.component.ts:208)
at GlobeComponent.ngAfterViewInit (globe.component.ts:75)
at callHook (core.js:2573)
at callHooks (core.js:2542)
at executeInitAndCheckHooks (core.js:2493)
at refreshView (core.js:9537)
at refreshComponent (core.js:10637)
at refreshChildComponents (core.js:9263)
at refreshView (core.js:9516)
at renderComponentOrTemplate (core.js:9580)
Any help would be much appreciated! Thanks in advance.
I found a solution:
I've created a service that reads the JSON file through the HttpClient and returns the array of objects stores in your file.
This is the service:
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class LocalJSONService {
constructor(private http: HttpClient) { }
getLocalJSON$(): Observable<any>{
return this.http.get(`../assets/countries.json`);
}
}
And this is the globe.component.ts:
First inject the new service on the controller:
constructor(private localJSON: LocalJSONService) {
...
}
And then on the ngOnInit (you may place it on the ngAfterViewInit probably) I call a function called getLocalJSON$:
getLocalJSON$(): void {
this.localJSON
.getLocalJSON$()
.pipe(first())
.subscribe((countries: any[]) => {
countries.map((country: any) => console.log(country));
})
}
Instead of iterate over the countries you can store the values or call another function, whatever you need.
I have several model.ts files.
When I use httpClient, I get a JSON object, but it does not work correctly since I have to deserialize them: How to recursively init class get by httpclient.
BUT since so, I found the project "class-transformer" that help me deserialize all my models.
I have in my services:
public method(cli: any): Observable<A> {
const formData = new FormData();
formData.append('cli', JSON.stringify(cli));
return this.http.post<A>('/my/url',
formData, {
withCredentials: true
}).pipe(first(),
map(res => {
return plainToClass(A, res);
})
);
}
And for models something like:
// A.model.ts
import { Type } from 'class-transformer';
import { B } from './B.model';
export class A {
// Some properties
#Type(() => B)
b: B[]
// Some methods
}
And B
// B.model.ts
import { Type } from 'class-transformer';
import { A } from './A.model';
export class B {
// Some properties
#Type(() => A)
a: A[]
// Some methods
}
But, when compiling I got "Circular dependency" and indeed there is a circular dependency...
Looking for solution I understant that I can use a Barrel (https://github.com/typestack/class-transformer/issues/230) but it did not work.
My only constraint is that I have to keep this relation -> (or something really similar since I can not modify the backend and so data I will receive with httpClient).
Any idea on how to fix the cyclic dependency?
Finally, I used the barrel solution. It appears that in the rest of my code there were import of class A directly, I changed them to use the barrel and everything works (I still have warnings. But it works)
// C.model.ts
// the problem is here
import { A } from './bla/A.model';
export class C {
}
// C.model.ts
import { A } from './bla';
export class C {
}
And at ./bla I have a index.ts with all the model export
I have a service performing http.get on a Drupal API and retrieving JSON data.
The component utilising that JSON data keeps generating the following error:
ERROR in src/app/form-test/form-test.component.ts(18,28): error TS2551: Property 'included' does not exist on type 'Question[]'. Did you mean 'includes'?
From the following code:
constructor(private dataService: QuizService) { }
ngOnInit() {
this.dataService.fetch().subscribe(data => {
this.jsondata = data.included[0].attributes.field_json;
console.log(data, ': DATA');
});
}
I don't understand why there is a problem with the JSON and why it's trying to find includes instead of included in the JSON structure. Below is a screenshot of a sample of the JSON:
I have confirmed the structure of the JSON data (as confirmed from the image above), also from console logging the JSON data and that the API URL is live at the time Ay angular app is attempting to call it.
Can anyone advice what is the cause of this error and how can I resolve it?
UPDATE:
quiz.service.ts:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs';
export interface Question {
// title: string;
question: string;
included: any[];
}
#Injectable({
providedIn: 'root'
})
export class QuizService {
// tslint:disable-next-line: max-line-length
private quizURL: string = 'http://drupal-8-composer-drupal-test.com/jsonapi/node/quiz/31f020f7-34d9-4b9a-bd2b-0d567eb285dc/?include=field_questions&fields%5Bnode--quiz%5D=title,drupal_internal__nid,body&fields%5Bnode--question%5D=title,field_processed,body,field_options,field_json';
constructor(private httpClient: HttpClient) { }
fetch(): Observable<Question[]> {
return this.httpClient.get<Question[]>( this.quizURL );
}
}
The error states that data has type Question[]. It is an array, not an object. Typescript compiler tries to find an included variable in array and there's none. So it gives you an error.
Your JSON structure contains an array of questions in the included field. So the type which the fetch returns should be like { included: Question[] }:
fetch(): Observable<{ included: Question[] }> {
return this.httpClient.get<{ included: Question[] }>( this.quizURL );
}
Or you can process the response in service and return questions only:
fetch(): Observable<Question[]> {
return this.httpClient.get(this.quizURL)
.pipe(map((data: { included: Question[] }) => data.included));
}
.map operator gets the whole response object, extracts only questions and returns them as array.
I am currently trying to unit test a container that pulls in a static JSON file of phone numbers and passes it to the component to display, however I am not sure how I should go about testing it. The code for the container is as follows:
import React from 'react';
import data from *JSON file location*
import CountryInfo from *component for the country information* ;
class CountryInfoContainer extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
numbersJson: null
};
}
async componentWillMount() {
const numbersJson = data;
this.setState({ numbersJson });
}
render() {
return (
<CountryInfo json={this.state.numbersJson} showText={this.props.showText} />
);
}
}
export default CountryInfoContainer;
I currently have my unit test to look like this
import React from 'react';
import Adapter from 'enzyme-adapter-react-16';
import { mount, configure } from 'enzyme';
import { MemoryRouter } from 'react-router-dom';
import CountryInfoContainer from './CountryInfoContainer';
configure({ adapter: new Adapter() });
describe('Successful flows', () => {
test('checks if json has null entries', () => {
const wrapper = mount(<MemoryRouter><CountryInfoContainer /></MemoryRouter >);
const data = wrapper.find(numbersJson);
// eslint-disable-next-line no-console
console.log(data.debug);
});
});
Obviously, it doesn't work now because I am not sure how to use the variable numbersJson in the container in the test file or how to check if it is null.
The variable numbersJson is not defined in the scope of your test. If I understand correctly, you are testing that when you first mount the component, that it's state contains a null value for the numbersJson key.
First of all, you need to mount your component directly without MemoryRouter:
const wrapper = mount(<CountryInfoContainer />);
Then you can write an expect() for the state:
expect(wrapper.state().numbersJson).toBeNull();
I want to fetch all data from "https://blockchain.info/api/exchange_rates_api" and show that on Page. I tried it but got an error message. Here is my Code :
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(){
super();
this.state = {
data: []
}
}
componentDidMount()
{
fetch("https://blockchain.info/ticker").
then((Response) => Response.json()).
then ((findresponse)=>
{
console.log(findresponse)
this.setState({
data:findresponse
});
})
}
render()
{
return(
<div>
{
this.state.data.map((dynamicData, Key) =>
<div>
<span>{dynamicData}</span>
</div>
)
}
</div>
)
}
}
export default App;
I got an error in setState method. When I m trying to write without setState method, I got data in the console. But I want data on the page in Table form.
You are getting an object from the API call but you need an array in order to use map, so you need to do this:
fetch("https://blockchain.info/ticker").
then((Response) => Response.json()).
then ((findresponse)=>
{
this.setState({
data: [findresponse] //wrap findresponse brackets to put the response in an array
});
})
Problem is that what you receive as JSON response from api call is an object not array. Objects don't have defined map function. First you need to convert object into an array.