what if we accidently don't use '()' after calling the method - javascript

What if we forget put the parentheses after calling function, like when we have
renderContent(){}
and we want to call it in a div
<div>{this.renderContent()}</div>
what if we forget write ()?
it shows no error and shows nothing in the screen
and when we put () after calling, it shows if I got a problem like this how can I know where I didn't put ()
if you need more information please let me know

All that will happen in this specific case is it will print the function's code. So for example if you have a function like
renderContent = () => {
console.log("hi");
return <p>Hello world</p>;
}
Then using it without the parenthesis will just print the contents of the function, whereas using the parenthesis will print the JSX for the p tag <div>{this.renderContent()}</div>
will return <p>Hello world</p>
within the div, whereas without the parenthesis, it will just print
() => {
console.log("hi");
return <p>Hello world</p>;
}
which is the function's contents

import React, { Component } from 'react';
class Content extends Component {
renderContent = () => <h1>Hi</h1>
render() {
return (
<div>{this.renderContent()}</div>
)
}
}
export default Content;
This will show "Hi" in the screen.
But if you've deleted () from this.renderContent():
import React, { Component } from 'react';
class Content extends Component {
renderContent = () => <h1>Hi</h1>
render() {
return (
<div>{this.renderContent}</div>
)
}
}
export default Content;
It's showing nothing on the screen, and you would have an error in the console:
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.
Because it supposed to return the structure of the function (if in vanilla javascript), but this is not allowed in React js

Related

How to pass the 'onClick' function to child component in React

I create a very generic Modal that can get different header, body and footer, but also different arguments for their Reactstrap components (I'm using Reactstrap to create the Modal but the question needn't be specific to solve a Reactstrap problem).
My GenericModal.js code looks like:
class GenericModal extends React.Component{
render(){
return(
<Reactstrap.Modal {...this.props.other} />
<Reactstrap.ModalHeader {...this.props.headerArgs}>{this.props.header}</Reactstrap.ModalHeader>
<Reactstrap.ModalBody {...this.props.bodyArgs}>{this.props.body}</Reactstrap.ModalBody>
<Reactstrap.ModalFooter {...this.props.footerArgs}>{this.props.footer}</Reactstrap.ModalFooter>
</Reactstrap.Modal>);
}
}
And so I call this class like this:
<GenericCard {...{other: other, headerArgs: headerArgs, bodyArgs: bodyArgs, footerArgs: footerArgs,
cardheader:header, cardbody:body, cardfooter:footer}} />
Now I know that this method works because I've tried it with className, for example:
const bodyArgs = {className: 'my-5'};
I want to also be able to pass an onClick function - but not merely the function (as we can see in this question), but the whole thing: onClick=foo().
I'm having a bit of a problem understanding how I can put the onClick method inside a json-style format like I did with className.
I can't write an anonymous function for the onClick inside const bodyArgs = {...}, and writing it as
const bodyArgs = {onClick: {foo}};
Provides an undefined foo. I also can't put this.foo because it's an unexpected syntax as well.
Any thoughts?
Welp, found the solution moments after I posted this.
Just didn't need the {} curly brackets.
const bodyArgs = {onClick: this.foo};
Does the job.
Thought I'd keep it here in case anyone stumbles into this issue.
This should work as you have explained but I cannot fully know without the whole example. Here is a working bit of code and a codesandbox link of what you are tying to do.
import React from "react";
import "./styles.css";
class ClickExecutor extends React.Component {
render() {
return (
<div>
<h4>Click Executor</h4>
<div>
<button onClick={() => this.props.bodyArgs.alert1()}>One</button>
<button onClick={() => this.props.bodyArgs.alert2()}>Two</button>
</div>
</div>
);
}
}
class GenericModal extends React.Component {
alert1 = () => {
alert("Alert 1");
};
alert2 = () => {
alert("Alert 2");
};
render() {
const bodyArgs = {
alert1: this.alert1,
alert2: this.alert2
};
return (
<div>
<h1>Generic Modal</h1>
<ClickExecutor
{...{
bodyArgs: bodyArgs,
otherProps: "other various properties ..."
}}
/>
</div>
);
}
}
export default function App() {
return (
<div className="App">
<GenericModal />
</div>
);
}
Working Demo LINK : https://codesandbox.io/s/smoosh-frost-rj1vb

Grandparent component doesn't pass argument to function

I have some doubts trying to understand how react works with functions as props, passing them around to child components. I already saw some tutorials but didn't grasp my issue at the moment.
Basically I have a simple component that passes a list down, and other component that handles the list with Array.map to render another component.
Basically I have the following:
App.js -> Quotes -> Quote.
And I want to handle the click on the Quote component. So everytime the user clicks Quote I want to handle it on the APP.js.
I already tried to pass the reference as a prop down and in the app.js quotes component to call the function, but it didn't work.
This is what I tried till now:
App.js
import React, { Component } from 'react';
import classes from './App.module.css';
import Quotes from '../components/quotes/quotes'
class App extends Component {
state = {
quotes: [
{ id: 1, text: "Hello There 1" },
{ id: 2, text: "Hello There 2" },
{ id: 3, text: "Hello There 3" },
{ id: 4, text: "Hello There 4" }
],
clickedQuote: "none"
}
handleClickedQuote (id) {
console.log(id)
const quoteIndex = this.state.quotes.findIndex(q => q.id === id)
this.setState({
clickedQuote: this.state.quotes[quoteIndex].text
})
}
render() {
return (
<div className="App">
<div className={classes['quotes-wrapper']}>
<Quotes clicked={this.handleClickedQuote} quotes={this.state.quotes}/>
<p>clicked quote {this.state.clickedQuote}</p>
</div>
</div>
);
}
}
export default App;
Quotes.js
import React from 'react';
import Quote from './quote/quote'
const quotes = (props) => props.quotes.map((quote) => {
return (
<Quote clicked={props.clicked} text={quote.text}/>
)
})
export default quotes
Quote.js
import React from 'react';
import classes from './quote.module.css'
const quote = (props) => {
return (
<div onClick={() => props.clicked(props.id)} className={classes.quote}>
<p className={classes['quote-text']}>{props.text}</p>
</div>
)
}
export default quote
I need to get the id on the hanleClickedQuote in the App.js function. What am I doing wrong?
You need to explicitly pass the id as a prop. So, in Quotes.js in your map(),
something like:
<Quote id={quote.id} clicked={props.clicked} text={quote.text}/>
Update: as #Ashkan said in their answer, you also need to properly bind your handler.
Well there are two things going very wrong in your code. First one is a common problem in JS community. I suggest you read deeper into the usage of the 'this' keyword. in App.js you are defining your method as a function declaration.
handleClickedQuote(id) {
console.log(id)
const quoteIndex = this.state.quotes.findIndex(q => q.id === id)
this.setState({
clickedQuote: this.state.quotes[quoteIndex].text
})
}
Now the 'this' keyword in function declarations are dynamically set, meaning 'this' here actually gets set when the function gets called and since it's an event handler, the value of 'this' will actually be your event! You can test it out. But we want 'this' to refer to our class so we can access the state.
There are two ways to fix this, first:
You can bind the correct value for 'this' in the constructor of your App.js class like this (the old way):
constructor(props) {
super(props);
this.handleClickedQuote = this.handleClickedQuote.bind(this);
}
This replaces the method in your class with a version that uses the correct 'this' value at the construction step of your object.
Or simpler yet, you can use an arrow function since the 'this' keyword in an arrow function is set lexically:
handleClickedQuote = id => {
console.log(id);
const quoteIndex = this.state.quotes.findIndex(q => q.id === id);
this.setState({
clickedQuote: this.state.quotes[quoteIndex].text
});
}
NOTE: In an arrow function the value of 'this' basically refers to whatever is outside of that code block which in this case is your entire object.
Also you have a minor mistake in your code which someone mentioned. You actually forgot to pass the id of the quote as a prop to the Quote component. But that's just a minor oversight.
It's important to know this problem has less to do with React than JS itself. My advice is getting a little deeper into JS and learning all the quirks and technicalities of the language. It will save you a lot of hassle in the future. Also work on your debugging skills so mistakes like the missing prop don't fall through the cracks so easily.
Best of luck

Get HTML from database into React

I have the following JSON from my test API:
{
"/page-one": "<div><h1 className='subpage'>Database page one!</h1><p className='subpage'>Showing possibility of page one</p><p className='subpage'>More text</p></div>",
"/page-two": "<div><h1 className='subpage'>Database page two!</h1><p className='subpage'>Showing possibility of page two</p><p className='subpage'>More text</p></div>",
"/page-two/hej": "<div><h1 className='subpage'>Database page two supbage hej!</h1><p className='subpage'>Showing possibility of page two with subpage</p><p className='subpage'>More text</p></div>"
}
I want do create a dynamig page that uses the URL pathname to determine which HTML should be used/displayed. I have tried the following:
import React from 'react';
import Utils from "../../utils";
import './not-found-controller.css';
class GenericController extends React.Component {
constructor(props) {
super(props);
this.getPage = this.getPage.bind(this);
}
async getPage() {
const pageList = await Utils.http.get('http://demo5369936.mockable.io/pages');
console.log(pageList.data);
const possiblePages = Object.keys(pageList.data);
if (possiblePages.indexOf(window.location.pathname) !== -1) {
return pageList.data[window.location.pathname];
} else {
return (
<div>
<h1 className="subpage">404 - Page not fpund!</h1>
<p className="subpage">The page you are looking for is not found. Please contact support if you thing this is wrong!</p>
</div>
);
}
}
render() {
return (
<section className="not-found-controller">
{ this.getPage }
</section>
);
}
}
export default GenericController;
But this does not work. I only get the following error:
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.
in section (at not-found-controller.js:31)
in NotFoundController (at app.js:166)
in Route (at app.js:164)
in Switch (at app.js:88)
in div (at app.js:87)
in App (created by Route)
in Route (created by withRouter(App))
in withRouter(App) (at src/index.js:18)
in Router (created by BrowserRouter)
in BrowserRouter (at src/index.js:17)
The API call is correct, the console.log(pageList.data); prints the JSON correctly. I tried to search for how to include HTML like this but I did not find any answer that made me understand.
3 things wrong here, but your browser's console should've told you the errors already.
First, your getPage method is async, but React rendering passes should be synchronous and execute immediately with whatever data is present. If the data is not present, the component should still render (you can return null from render to render "nothing"). Instead, fetch your data outside of a render pass and call setState when the data is available.
Second, getPage is returning a HTML string, which is not going to be parsed this way, but rendered as-is. You need to use the dangerouslySetInnerHTML prop.
Third, as jitesh pointed out, you are missing the function call brackets.
All things considered, you need something like the following (just thrown this together real quick, but you should get the idea):
constructor(props, context) {
super(props, context);
this.state = {
pageData: undefined
};
}
componentDidMount() {
this.getPage();
}
async getPage() {
const pageList = await Utils.http.get('http://demo5369936.mockable.io/pages');
this.setState({
pageData: pageList.data[window.location.pathname]
});
}
render() {
if (this.state.pageData) {
return (
<section
className="not-found-controller"
dangerouslySetInnerHTML={{ __html: this.state.pageData }}
/>
);
} else {
// Not found or still loading...
}
}
1) Use state to put loaded data to render. Like this.setState({pageHtml: pageList.data})
2) Use dangerouslySetInnerHTML to render your own html https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml

Why this error is showing: "A valid React element (or null) must be returned. You may have returned undefined"

I know this question has been answered but i just cannot handle what's going so wrong. I'm having a wrapper function:
const withTracker = (WrappedComponent, partnerTrackingCode, options = {}) => {
const trackPage = (page) => {
ReactGA.set({
page,
options
});
ReactGA.pageview(page);
};
class HOC extends Component {
componentDidMount() {
ReactGA.initialize(partnerTrackingCode);
const page = this.props.location.pathname;
trackPage(page);
}
componentWillReceiveProps(nextProps) {
const currentPage = this.props.location.pathname;
const nextPage = nextProps.location.pathname;
if (currentPage !== nextPage) {
trackPage(nextPage);
}
}
render() {
return <WrappedComponent {...this.props} />;
}
}
return HOC;
};
export default withTracker;
and i'm calling it here:
export default (props) => {
const MainComponent = (
<div>
...
</div>
);
if (props.partnerTrackingCode) {
return (
withTracker(MainComponent, props.partnerTrackingCode)
);
}
return (<div />);
};
When the tracking code is defined and the withTracker is called even if mainComponent is a component it shows me this error: A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object
I've also try to replace the WrappedComponent with an empty div:
return(<div />)
but still the same error
It looks like you're confusing elements and components here. You're passing around elements (the actual output you want to be rendered), whereas a HOC is a component (a function that generally takes a set of props and returns an element). You're passing an element to your HOC, so when it tries rendering it (in the HOC render function) it can't render it and you get the error.
To fix, you'd firstly need to make your MainComponent into an actual component instead of just the element you want it to return, e.g.:
const MainComponent = props => (
<div>
...
</div>
)
Then to use that with your wrapper you'd want to wrap and then render that:
if (props.partnerTrackingCode) {
const MainWithTracker = withTracker(MainComponent, props.partnerTrackingCode)
return <MainWithTracker />;
}
This is a bit weird though, as you need to create the wrapped component within your render method, which isn't how you'd normally do things. It might make more sense to change your HOC so that it returns a component that takes the partnerTrackingCode as a prop instead of an argument to your HOC. Something along the lines of:
// your HOC (omitting irrelevant bits)
const withTracker = (WrappedComponent, options = {}) => {
...
class HOC extends Component {
componentDidMount() {
ReactGA.initialize(this.props.partnerTrackingCode);
...
}
...
render() {
// pull out the tracking code so it doesn't get passed through to the
// wrapped component
const { partnerTrackingCode, ...passthrough } = this.props;
return <WrappedComponent {...passthrough} />;
}
}
return HOC;
};
// in your component
const MainComponent = props => (
<div>
...
</div>
);
const MainWithTracker = withTracker(MainComponent);
export default (props) => {
if (props.partnerTrackingCode) {
return (<MainWithTracker partnerTrackingCode={props.partnerTrackingCode} />);
}
return (<div />);
};
(I don't think this is the best way to do it, I've just tried keeping as close to your code as I could. Once you start restructuring it, with your better knowledge of exactly what you're trying to do you may find a better way to organise it.)
your problem in your return method , in first step you must be know
when you want call HOC , you must write like this
return withTracker(MainComponent, props.partnerTrackingCode)
instead this
return (
withTracker(MainComponent, props.partnerTrackingCode)
);
remove ()
and then check again , if you still have error tell me

Render child components with Enzymejs tests

I'm trying to test a simple component that take some props (it have no state, or redux connection) with Enzyme, it works for the plain elements like <div /> and so on, but when i try to test if the element rendered by the child component exists, it fails.
I'm trying to use mount but it spit me a lot of errors, i'm new in this so, here is my code:
import React, { Component } from 'react';
import WordCloud from 'react-d3-cloud';
class PredictWordCloud extends Component {
render() {
const fontSizeMapper = word => Math.log2(word.value) * 3.3;
const { size, data, show } = this.props;
if (!show)
return <h3 className='text-muted text-center'>No data</h3>
return (
<section id='predict-word-cloud'>
<div className='text-center'>
<WordCloud
data={data}
fontSizeMapper={fontSizeMapper}
width={size}
height={300} />
</div>
</section>
)
}
}
export default PredictWordCloud;
It's just a wrapper for <WordCloud />, and it just recieves 3 props directly from his parent: <PredictWordCloud data={wordcloud} size={cloudSize} show={wordcloud ? true : false} />, anything else.
The tests is very very simple for now:
import React from 'react';
import { shallow } from 'enzyme';
import PredictWordCloud from '../../components/PredictWordCloud.component';
import cloudData from '../../helpers/cloudData.json';
describe('<PredictWordCloud />', () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<PredictWordCloud data={cloudData} size={600} show={true} />)
});
it('Render without problems', () => {
const selector = wrapper.find('#predict-word-cloud');
expect(selector.exists()).toBeTruthy();
});
});
For now it pass but if we change the selector to: const selector = wrapper.find('#predict-word-cloud svg'); where the svg tag is the return of <Wordcloud /> component, the tests fails because the assertion returns false.
I tried to use mount instead of shallow, exactly the same test, but i get a big error fomr react-d3-cloud:
PredictWordCloud Render without problems TypeError: Cannot read property 'getImageData' of null.
This is specially weird because it just happens in the test environment, the UI and all behaviors works perfectly in the browser.
You can find your component directly by Component name.
Then you can use find inside your sub-component as well.
e.g
it('Render without problems', () => {
const selector = wrapper.find('WordCloud').first();
expect(selector.find('svg')).to.have.length(1);
});
or
You can compare generated html structure as well via
it('Render without problems', () => {
const selector = wrapper.find('WordCloud').first();
expect(selector.html()).to.equal('<svg> Just an example </svg>');
});

Categories