How to call an api containing array of strings through my react code - reactjs

I need to call an api which consists of an array of string. I need to then publish the response from the api in a dropdown menu. Below is what the API holds that I need to call-
Sample api data - [“Leanne Graham”,”Ervin Howell”,”Patricia”]
Below sample code has the API which holds object information
import React, { Component } from "react";
import "../styles/schema.css";
import Params1 from "../components/Params1";
import axios from 'axios';
import Select from "react-select";
class New extends Component {
constructor(props) {
super(props);
this.handleStoreprocSelection = this.handleStoreprocSelection.bind(this);
this.state = {
selectStoreprocOptions : [],
id: "",
name: '',
itemSelected:false
}
}
async getStoreProcOptions(){
const resSchema = await axios.get('https://jsonplaceholder.typicode.com/users') --backend API call in object format
const data = resSchema.data
const options = data.map(d => ({
"value" : d.id,
"label" : d.name
}))
this.setState({selectStoreprocOptions: options})
}
handleStoreprocSelection(){
// alert('You selected me!!')
this.setState({itemSelected: true});
}
componentDidMount() {
// get all entities - GET
this.getStoreProcOptions()
}
render() {
const itemSelected = this.state.itemSelected;
let param;
if (itemSelected) {
param = <Params1 />;
}
return (
<div>
<div>
<form id ="store-proc">
<label>STORED PROCEDURE</label>
<Select id="store-proc-select" options={this.state.selectStoreprocOptions} onChange={this.handleStoreprocSelection} /> --my dropdown
</form>
</div>
{param}
</div>
);
}
}
export default New;

You need a state, let's say an empty array.
You need to call that API, using some of the methods, for example browser built in fetch or 3rd party library axios.
Then you need to update your state with the response you will get from your API.
Finally use your state inside of your component and display whatever you want.
These are the steps you need to follow, if you needed some logic. Since you didn't provide any code, I assume you didn't know from where to start. If you share some code, will be possible to help more.

are you using any library? because the plain HTML form select would be written in lower case <select/>, not <Select/>. if so, please state it out.
in plain HTML: the solution would be to map the array elements into <option/>. so, selectStoreprocOptions from state, as assigned here: this.setState({selectStoreprocOptions: options}).
inside render:
<select>
{
this.state.selectStoreprocOptions.map(selectStoreprocOption => (<option ..> .. </option>)
}
</select>
Edit: Sorry, I've overseen the use of react-select. never used it, according to the API doc it looks good to me. have you checked that your state really contains an array with the expected objects?
probably ignore my post then, sorry again xD

Related

Making .map inside .map [duplicate]

In my component's render function I have:
render() {
const items = ['EN', 'IT', 'FR', 'GR', 'RU'].map((item) => {
return (<li onClick={this.onItemClick.bind(this, item)} key={item}>{item}</li>);
});
return (
<div>
...
<ul>
{items}
</ul>
...
</div>
);
}
everything renders fine, however when clicking the <li> element I receive the following error:
Uncaught Error: Invariant Violation: Objects are not valid as a React
child (found: object with keys {dispatchConfig, dispatchMarker,
nativeEvent, target, currentTarget, type, eventPhase, bubbles,
cancelable, timeStamp, defaultPrevented, isTrusted, view, detail,
screenX, screenY, clientX, clientY, ctrlKey, shiftKey, altKey,
metaKey, getModifierState, button, buttons, relatedTarget, pageX,
pageY, isDefaultPrevented, isPropagationStopped, _dispatchListeners,
_dispatchIDs}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from
the React add-ons. Check the render method of Welcome.
If I change to this.onItemClick.bind(this, item) to (e) => onItemClick(e, item) inside the map function everything works as expected.
If someone could explain what I am doing wrong and explain why do I get this error, would be great
UPDATE 1:
onItemClick function is as follows and removing this.setState results in error disappearing.
onItemClick(e, item) {
this.setState({
lang: item,
});
}
But I cannot remove this line as I need to update state of this component
I was having this error and it turned out to be that I was unintentionally including an Object in my JSX code that I had expected to be a string value:
return (
<BreadcrumbItem href={routeString}>
{breadcrumbElement}
</BreadcrumbItem>
)
breadcrumbElement used to be a string but due to a refactor had become an Object. Unfortunately, React's error message didn't do a good job in pointing me to the line where the problem existed. I had to follow my stack trace all the way back up until I recognized the "props" being passed into a component and then I found the offending code.
You'll need to either reference a property of the object that is a string value or convert the Object to a string representation that is desirable. One option might be JSON.stringify if you actually want to see the contents of the Object.
So I got this error when trying to display the createdAt property which is a Date object. If you concatenate .toString() on the end like this, it will do the conversion and eliminate the error. Just posting this as a possible answer in case anyone else ran into the same problem:
{this.props.task.createdAt.toString()}
I just got the same error but due to a different mistake: I used double braces like:
{{count}}
to insert the value of count instead of the correct:
{count}
which the compiler presumably turned into {{count: count}}, i.e. trying to insert an Object as a React child.
Just thought I would add to this as I had the same problem today, turns out that it was because I was returning just the function, when I wrapped it in a <div> tag it started working, as below
renderGallery() {
const gallerySection = galleries.map((gallery, i) => {
return (
<div>
...
</div>
);
});
return (
{gallerySection}
);
}
The above caused the error. I fixed the problem by changing the return() section to:
return (
<div>
{gallerySection}
</div>
);
...or simply:
return gallerySection
React child(singular) should be type of primitive data type not object or it could be JSX tag(which is not in our case). Use Proptypes package in development to make sure validation happens.
Just a quick code snippet(JSX) comparision to represent you with idea :
Error : With object being passed into child
<div>
{/* item is object with user's name and its other details on it */}
{items.map((item, index) => {
return <div key={index}>
--item object invalid as react child--->>>{item}</div>;
})}
</div>
Without error : With object's property(which should be primitive, i.e. a string value or integer value) being passed into child.
<div>
{/* item is object with user's name and its other details on it */}
{items.map((item, index) => {
return <div key={index}>
--note the name property is primitive--->{item.name}</div>;
})}
</div>
TLDR; (From the source below) : Make sure all of the items you're rendering in JSX are primitives and not objects when using React. This error usually happens because a function involved in dispatching an event has been given an unexpected object type (i.e passing an object when you should be passing a string) or part of the JSX in your component is not referencing a primitive (i.e. this.props vs this.props.name).
Source - codingbismuth.com
Mine had to do with forgetting the curly braces around props being sent to a presentational component:
Before:
const TypeAheadInput = (name, options, onChange, value, error) => {
After
const TypeAheadInput = ({name, options, onChange, value, error}) => {
I too was getting this "Objects are not valid as a React child" error and for me the cause was due to calling an asynchronous function in my JSX. See below.
class App extends React.Component {
showHello = async () => {
const response = await someAPI.get("/api/endpoint");
// Even with response ignored in JSX below, this JSX is not immediately returned,
// causing "Objects are not valid as a React child" error.
return (<div>Hello!</div>);
}
render() {
return (
<div>
{this.showHello()}
</div>
);
}
}
What I learned is that asynchronous rendering is not supported in React. The React team is working on a solution as documented here.
Mine had to do with unnecessarily putting curly braces around a variable holding a HTML element inside the return statement of the render() function. This made React treat it as an object rather than an element.
render() {
let element = (
<div className="some-class">
<span>Some text</span>
</div>
);
return (
{element}
)
}
Once I removed the curly braces from the element, the error was gone, and the element was rendered correctly.
For anybody using Firebase with Android, this only breaks Android. My iOS emulation ignores it.
And as posted by Apoorv Bankey above.
Anything above Firebase V5.0.3, for Android, atm is a bust. Fix:
npm i --save firebase#5.0.3
Confirmed numerous times here
https://github.com/firebase/firebase-js-sdk/issues/871
I also have the same problem but my mistake is so stupid. I was trying to access object directly.
class App extends Component {
state = {
name:'xyz',
age:10
}
render() {
return (
<div className="App">
// this is what I am using which gives the error
<p>I am inside the {state}.</p>
//Correct Way is
<p>I am inside the {this.state.name}.</p>
</div>
);
}
}
Typically this pops up because you don't destructure properly. Take this code for example:
const Button = text => <button>{text}</button>
const SomeForm = () => (
<Button text="Save" />
)
We're declaring it with the = text => param. But really, React is expecting this to be an all-encompassing props object.
So we should really be doing something like this:
const Button = props => <button>{props.text}</button>
const SomeForm = () => (
<Button text="Save" />
)
Notice the difference? The props param here could be named anything (props is just the convention that matches the nomenclature), React is just expecting an object with keys and vals.
With object destructuring you can do, and will frequently see, something like this:
const Button = ({ text }) => <button>{text}</button>
const SomeForm = () => (
<Button text="Save" />
)
...which works.
Chances are, anyone stumbling upon this just accidentally declared their component's props param without destructuring.
Just remove the curly braces in the return statement.
Before:
render() {
var rows = this.props.products.map(product => <tr key={product.id}><td>{product.name}</td><td>{product.price}</td></tr>);
return {rows}; // unnecessary
}
After:
render() {
var rows = this.props.products.map(product => <tr key={product.id}><td>{product.name}</td><td>{product.price}</td></tr>);
return rows; // add this
}
I had the same problem because I didn't put the props in the curly braces.
export default function Hero(children, hero ) {
return (
<header className={hero}>
{children}
</header>
);
}
So if your code is similar to the above one then you will get this error.
To resolve this just put curly braces around the props.
export default function Hero({ children, hero }) {
return (
<header className={hero}>
{children}
</header>
);
}
I got the same error, I changed this
export default withAlert(Alerts)
to this
export default withAlert()(Alerts).
In older versions the former code was ok , but in later versions it throws an error. So use the later code to avoid the errror.
This was my code:
class App extends Component {
constructor(props){
super(props)
this.state = {
value: null,
getDatacall : null
}
this.getData = this.getData.bind(this)
}
getData() {
// if (this.state.getDatacall === false) {
sleep(4000)
returnData("what is the time").then(value => this.setState({value, getDatacall:true}))
// }
}
componentDidMount() {
sleep(4000)
this.getData()
}
render() {
this.getData()
sleep(4000)
console.log(this.state.value)
return (
<p> { this.state.value } </p>
)
}
}
and I was running into this error. I had to change it to
render() {
this.getData()
sleep(4000)
console.log(this.state.value)
return (
<p> { JSON.stringify(this.state.value) } </p>
)
}
Hope this helps someone!
If for some reason you imported firebase. Then try running npm i --save firebase#5.0.3. This is because firebase break react-native, so running this will fix it.
In my case it was i forgot to return a html element frm the render function and i was returning an object . What i did was i just wrapped the {items} with a html element - a simple div like below
<ul>{items}</ul>
Just remove the async keyword in the component.
const Register = () => {
No issues after this.
In my case, I added a async to my child function component and encountered this error. Don't use async with child component.
I got this error any time I was calling async on a renderItem function in my FlatList.
I had to create a new function to set my Firestore collection to my state before calling said state data inside my FlatList.
My case is quite common when using reduce but it was not shared here so I posted it.
Normally, if your array looks like this:
[{ value: 1}, {value: 2}]
And you want to render the sum of value in this array. JSX code looks like this
<div>{array.reduce((acc, curr) => acc.value + curr.value)}</div>
The problem happens when your array has only one item, eg: [{value: 1}].
(Typically, this happens when your array is the response from server so you can not guarantee numbers of items in that array)
The reduce function returns the element itself when array has only one element, in this case it is {value: 1} (an object), it causes the Invariant Violation: Objects are not valid as a React child error.
You were just using the keys of object, instead of the whole object!
More details can be found here: https://github.com/gildata/RAIO/issues/48
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class SCT extends Component {
constructor(props, context) {
super(props, context);
this.state = {
data: this.props.data,
new_data: {}
};
}
componentDidMount() {
let new_data = this.state.data;
console.log(`new_data`, new_data);
this.setState(
{
new_data: Object.assign({}, new_data)
}
)
}
render() {
return (
<div>
this.state.data = {JSON.stringify(this.state.data)}
<hr/>
<div style={{color: 'red'}}>
{this.state.new_data.name}<br />
{this.state.new_data.description}<br />
{this.state.new_data.dependtables}<br />
</div>
</div>
);
}
}
SCT.propTypes = {
test: PropTypes.string,
data: PropTypes.object.isRequired
};
export {SCT};
export default SCT;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
If you are using Firebase and seeing this error, it's worth to check if you're importing it right. As of version 5.0.4 you have to import it like this:
import firebase from '#firebase/app'
import '#firebase/auth';
import '#firebase/database';
import '#firebase/storage';
Yes, I know. I lost 45 minutes on this, too.
I just put myself through a really silly version of this error, which I may as well share here for posterity.
I had some JSX like this:
...
{
...
<Foo />
...
}
...
I needed to comment this out to debug something. I used the keyboard shortcut in my IDE, which resulted in this:
...
{
...
{ /* <Foo /> */ }
...
}
...
Which is, of course, invalid -- objects are not valid as react children!
I'd like to add another solution to this list.
Specs:
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-redux": "^5.0.6",
"react-scripts": "^1.0.17",
"redux": "^3.7.2"
I encountered the same error:
Uncaught Error: Objects are not valid as a React child (found: object
with keys {XXXXX}). If you meant to render a collection of children,
use an array instead.
This was my code:
let payload = {
guess: this.userInput.value
};
this.props.dispatch(checkAnswer(payload));
Solution:
// let payload = {
// guess: this.userInput.value
// };
this.props.dispatch(checkAnswer(this.userInput.value));
The problem was occurring because the payload was sending the item as an object. When I removed the payload variable and put the userInput value into the dispatch everything started working as expected.
If in case your using Firebase any of the files within your project.
Then just place that import firebase statement at the end!!
I know this sounds crazy but try it!!
I have the same issue, in my case,
I update the redux state, and new data parameters did not match old parameters, So when I want to access some parameters it through this Error,
Maybe this experience help someone
My issue was simple when i faced the following error:
objects are not valid as a react child (found object with keys {...}
was just that I was passing an object with keys specified in the error while trying to render the object directly in a component using {object} expecting it to be a string
object: {
key1: "key1",
key2: "key2"
}
while rendering on a React Component, I used something like below
render() {
return this.props.object;
}
but it should have been
render() {
return this.props.object.key1;
}
If using stateless components, follow this kind of format:
const Header = ({pageTitle}) => (
<h1>{pageTitle}</h1>
);
export {Header};
This seemed to work for me
Something like this has just happened to me...
I wrote:
{response.isDisplayOptions &&
{element}
}
Placing it inside a div fixed it:
{response.isDisplayOptions &&
<div>
{element}
</div>
}

Call Typescript Apollo Query from componentDidMount()

I have a React Component that calls a Query that expects to receive an array of objects (for validation data). I then use it to validate an html form, that is in turn defined inside of an apollo Mutation element. The way it is structured within the return statement of the component render() method works but it looks cumbersome and it reminds me of the callback hell era. What I really want to do is to get rid of the <QueryCustomerValidations> element in the render() method and move it into (ideally) the componentDidMount lifecycle event. So that the validators can be loaded there for later use within the form, then leave the <MutationCreateCustomer>inside render().
Right now, I have to wrap the form inside the mutation. The mutation inside the arrow function that the query returns in order to receive the async data in the proper order.
//-------------------------------------------------------------------------
// Component Lifecycle Eventhandler Methods
//-------------------------------------------------------------------------
componentDidMount()
{
}
//-------------------------------------------------------------------------
// Render Method Section
//-------------------------------------------------------------------------
public render(): JSX.Element
{
// Return Form
return (
<React.Fragment>
{/* PAGE TITLE */}
<h2 className="text-center mb-3">Agregar Nuevo Cliente</h2>
{/* LOAD VALIDATIONS INTO STATE */}
<QueryCustomerValidations
query={Q_GET_CUSTOMER_VALIDATIONS}
>
{({ loading: loadingValidations, error: errorValidations, data: dataValidations }) =>
{
if (loadingValidations)
{
return "Cargando..."
}
if (errorValidations)
{
return `Error: ${errorValidations.message}`
}
if (dataValidations)
{
const validators: ValidationDescriptor[] = []
dataValidations.getCustomerValidations.forEach((validator) => {
validators.push(validator as ValidationDescriptor)
})
this.validators.setValidators(validators)
}
/* DATA ENTRY FORM */
return (
<div className="row justify-content-center">
<MutationCreateCustomer
mutation={M_CREATE_CUSTOMER}
onCompleted={() => this.props.history.push('/')}
>
{(createCustomer: any) => {
return (
<form name="frmNewCustomer"
className="col-md-8 m-3"
onSubmit={e => this.frmNewCustomer_submit(e, createCustomer)}
>
{ this.ctrl_form_layout(this.props, this.state, this.validators) }
</form>
)
}}
</MutationCreateCustomer>
</div>
)
}}
</QueryCustomerValidations>
</React.Fragment>
);
}
Here for documentation purposes are the interfaces to create the Query. Since I get some of this data from the server using the apollo client, a simple graphql query solution on the onDidMount() would not work in this case.
getCustomerValidations.ts (Interfaces)
// ====================================================
// GraphQL query operation: getCustomerValidations
// ====================================================
export interface getCustomerValidations_getCustomerValidations {
__typename: "ValidationDescriptor";
field: string | null;
type: string | null;
required: boolean;
max: number | null;
min: number | null;
regex: string | null;
}
export interface getCustomerValidations {
getCustomerValidations: getCustomerValidations_getCustomerValidations[];
}
customer-validations.query.ts (Client side query types)
//---------------------------------------------------------------------------------
// Imports Section (React/Apollo Libs)
//---------------------------------------------------------------------------------
import { gql } from 'apollo-boost';
import { Query } from 'react-apollo'
import { getCustomerValidations } from '../../typeDefs/operations/getCustomerValidations'
//---------------------------------------------------------------------------------
// GQL Query: Customers
//---------------------------------------------------------------------------------
export const Q_GET_CUSTOMER_VALIDATIONS = gql`
query getCustomerValidations {
getCustomerValidations
{
field
type
required
max
min
regex
}
}
`;
//---------------------------------------------------------------------------------
// Query Class: Customers
//---------------------------------------------------------------------------------
export class QueryCustomerValidations extends Query<getCustomerValidations> { }
The right solution can be a way to copy -and trigger- the <QueryCustomerValidations>element from the componentDidMount() method or, how to take the element away from the render() method and call it like with some sort of "await" way of doing it so it can be called first and the mutation after (and using the data from the query).
Thanks, I know this one is not really easy to figure out.
You're looking for 'old' (used before <Query/> component) HOC pattern (with compose) described here, here and here.
Composing "graphql(gqlquery ..." requested at start (without conditional skip option) with one or more (named) "gqlmutation ..." (called on demand) gives you a clear, readable solution.
compose(
graphql(Q_GET_CUSTOMER_VALIDATIONS),
graphql(gql`mutation { ... }`, { name: 'createSth' })
)(SomeComponent)
will pass data and createSth props to <SomeComponent/> then this.props.data object will contain loading, error and getCustomerValidations fields/properties. It's described here.
Query will be called at start (you can expect true in this.props.data.loading), no need to run query at cDM(). Mutation can be run using this.props.createSth() - custom name defined above (instead default prop name mutate).
Of course you can mix them with other required HOC's, f.e. redux connect(), withFormik() etc. - simply by adding single line of code.

React - populate component prop with data from HTTP endpoint

I’m a beginner with React, working on building my first real form after taking a few online classes.
I have a form that I use to record work unit goals and the daily “score” toward those goals. The form is working fine and data is arriving properly in my database. At the bottom of the form, I want to display the running total score for today.
I’m able to pull in my component that’s supposed to display the score, but I just can’t quite figure out how to populate that score component with the actual score value. I’ve tried numerous different methods of updating the “totalToday” value in state.data so that value will then be available as a prop. So far, nothing has worked.
Here’s my form file (with quite a bit of the irrelevant other code removed). You can see at the bottom of the form that I’m pulling in the “TotalToday” component and trying to send it the “totalToday” value as a prop.
import React from "react";
import Form from "./common/form";
…
import TotalToday from "./totalToday";
class FormProject extends Form {
state = {
data: {
inpDate: "",
selTeam: "",
selGoal: "",
selProject: "",
inpScore: "",
totalToday: ""
},
teams: [],
goals: [],
projects: []
};
async componentDidMount() {
await this.populateTeams();
await this.populateGoals();
await this.populateProjects();
}
doSubmit = async () => {
await saveScore(this.state.data);
this.setState({
data: {
inpDate: "",
selTeam: "",
selGoal: "",
selProject: "",
inpScore: "",
totalToday: ""
}
});
};
render() {
return (
<div>
<h1>Team Scorecard</h1>
<form onSubmit={this.handleSubmit}>
…
{this.renderButton("Save")}
</form>
<TotalToday totalToday={this.state.data.totalToday} />
</div>
);
}
}
export default FormProject;
Here’s my “TotalToday” component file –
import React from "react";
const TotalToday = ({ totalToday }) => {
return (
<div className="alert alert-primary mt-2" role="alert">
Total Time Today: {totalToday}
</div>
);
};
export default TotalToday;
Finally, here’s my “reportService.js” file that grabs the running total from my Express / Node backend. Note that my GET request works just fine in Postman, returning today’s total, so I know the back-end setup is working, at least –
import http from "./httpService";
import { apiUrl } from "../config.json";
export function getTotalToday() {
return http.get(apiUrl + "/report");
}
And here’s the layout of the data I’m getting back from my GET request –
[
{
"scoreTotalToday": 85
}
]
As I said at the top, I’ve tried all kinds of different ways of updating the state.data.totalToday data point in my form file and haven’t been able to make it work yet. What am I missing here?
Thanks in advance for your help!
Joel
I think your first step is to use axios.
I would write it like this and do it on componentDidMount lifecycle method.
componentDidMount() {
axios.get(apiUrl + "/reports")
.then(res => {
const totalToday = res.data;
this.setState({ totalToday });
})
}
I would also change the state object to
state = {
key : "someString",
key : [someArray]
}
Also I would delete the data : in your state object, it adds another step when locating the data you are looking for. Without the api to test it can't confirm this fixes everything, but this should get you on the right track. I think missing axios is the primary fix needed.
With a key assist from Josh above, I was able to solve my own problem and now have today’s total score showing up below my form!
Here are the changes I made.
First, I really just had to come to grips with the fact that I’m getting an array back, even though it’s an array that will only ever have one element. I come from a database background, so I was just locked in on the idea that I’d have one “cell” of data, which would be the total itself. But it’s still a key-value pair array, even though it’s just a one-element array.
So, I changed my state object in the form to move “totalToday” from the “data” section into the array section where I pull in the values for my selection boxes:
state = {
data: {
inpDate: "",
selTeam: "",
selGoal: "",
selProject: "",
inpScore: ""
},
teams: [],
goals: [],
projects: [],
totalToday: []
};
I then added back to my form file a helper function that I wasn’t sure I needed before –
async updateTotalToday() {
const { data: totalToday } = await getTotalToday();
this.setState({ totalToday });
}
Which I then added to my “doSubmit” function (as well as my componentDidMount lifecycle hook) so that that total will update every time I submit a new record (note that, again, I removed “totalToday” from the “data” section of the state update) –
doSubmit = async () => {
await saveScore(this.state.data);
this.setState({
data: {
inpDate: "",
selTeam: "",
selGoal: "",
selProject: "",
inpScore: ""
}
});
await this.updateTotalToday();
};
Then, at the bottom of my form, I just sent the entire array object as a prop to my component file (so, I removed the word “data” from my previous approach) –
{this.renderButton("Save")}
</form>
<TotalToday totalToday={this.state.totalToday} />
</div>
Finally, I added array mapping to my “TotalToday” component file. This is ultimately the most important piece to the whole solution –
import React from "react";
const TotalToday = ({ totalToday }) => {
return (
<div className="alert alert-primary mt-2" role="alert">
{totalToday.map((score, index) => (
<p key={index}>Total today: {score.scoreTotalToday}</p>
))}
</div>
);
};
export default TotalToday;
Thanks again to Josh for the help. I hope my struggle through this solution helps someone else!
Joel

How to implement GTM with reactjs

I am trying to implement GTM with reactjs. I have used react-google-tag-manager but it did not solve the purpose.
Somehow, the data layer needs to be in a particular format and also the needs to be right below the tag, but it is only one of them that i can achieve at a time.
I tried placing the code directly in template.html and call the function from the component i wanted, but that didn't work.
import React from 'react';
import gtmParts from 'react-google-tag-manager';
class GoogleTagManager extends React.Component {
componentDidMount() {
const dataLayerName = this.props.dataLayerName || 'dataLayer';
const scriptId = this.props.scriptId || 'react-google-tag-manager-gtm';
if (!window[dataLayerName]) {
const gtmScriptNode = document.getElementById(scriptId);
eval(gtmScriptNode.textContent);
}
}
render() {
const gtm = gtmParts({
id: this.props.gtmId,
sourcegroup: this.props.gtmGroupname,
sourceid:this.props.gtmSource,
age:this.props.age,
mtongue:this.props.gtmMtongue,
city:this.props.city,
});
return (
<div>
<div>{gtm.noScriptAsReact()}</div>
<div id={this.props.scriptId || 'react-google-tag-manager-gtm'}>
{gtm.scriptAsReact()}
</div>
</div>
);
}
}
export default GoogleTagManager;
I am pushing parameters in DataLayer and on checking on google tag assistant addon, whole the datalyer is empty.
I had this issue yesterday and to solve it, I had to put all the properties that I'm trying to record under the additionalEvents property. Something like this:
const gtm = gtmParts({
id: this.props.gtmId,
additionalEvents: {
sourcegroup: this.props.gtmGroupname,
sourceid:this.props.gtmSource,
age:this.props.age,
mtongue:this.props.gtmMtongue,
city:this.props.city
}
})
And also avoid using eval() since this is a dangerous pratique. Update your code like this:
if (!window[dataLayerName]) {
const script = document.createElement("script")
const gtmScriptNode = document.getElementById(scriptId)
const scriptText = document.createTextNode(gtmScriptNode.textContent)
script.appendChild(scriptText)
document.head.appendChild(script)
}

HOC/Render-Call Back or Library function?

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

Resources