react render values from an array gives error - reactjs

I have react component like:
class UserList extends Component {
render() {
const {users} = this.props
users.forEach(function(user){
console.log(user.name)
if(user.photos){
console.log(user.photos.data[0].source)
return (
<div>
<p>{user.name}</p>
</div>
)
}
})
}
}
export default UserList
But it is giving me error saying should return valid react component but returned array or undefined..
The return function should return the component right ?
What am I missing here ??

As the error states you need to return a valid React element.
The fix is to return a DOM node from your render, a div for example.
class UserList extends Component {
render() {
return <div></div>;
}
}
Further more using .forEach will not return a valid React element to the div, use .map instead to produce an array that you render.
class UserList extends Component {
render() {
return <div>{
this.props.users
.filter(user => user.photos)
.map((user) => {
return <li>{ user.name }</li>;
}}
</div>;
}
}

Related

How to limit queryselector to a react component

I have a react component with a list of child-components. In the child-component I want to target a specific DOM element e.g., to change the color in its ComponentDidMount method. How would I do this?
Parent component
export class ListComponent extends Component<...> {
render(): ReactNode {
return (
<div>
<ListItemComponent key="123"/>
<ListItemComponent key="456"/>
<ListItemComponent key="789"/>
</div>
);
}
}
Child component
export class ListComponent extends Component<...> {
componentDidMount(): void {
// const elementToChange = document.queryselector(".toTarget"); // Only works for the first element as it only targets the first on the page
const elementToChange = THISREACTCOMPONENT.queryselector(".toTarget");
elementToChange.style.backgroundColor = "123123";
}
render(): ReactNode {
return (
<div>
<div className="toTarget">
</div>
);
}
}
So, the question is, what should be instead of THISREACTCOMPONENT? How to target an element exclusively within the react component?
use a react ref.
Refs were created so you won't have to use queryselector, as interacting directly with the dom may lead to react bugs further down the line.
export class ListComponent extends Component<...> { {
constructor(props) {
super(props);
this.myRef = React.createRef(); // Get a reference to a DOM element
}
componentDidMount(): void {
const elementToChange = this.myref.current;
elementToChange.style.backgroundColor = "123123";
}
render() {
return (
<div>
<div className="toTarget" ref={this.myRef}> // binds this element to the this.myref variable
</div>
)
}
}
You could use Document.querySelectorAll to get all matching elements
document.querySelectorAll returns an array of matching element.
Then you would do it like so:
componentDidMount(): void {
const elements = document.querySelectorAll(".toTarget");
elements.forEach((el) => {
el.style.backgroundColor = "123123";
});
}

React componentDidUpdate() does not fire

I have an react app with primereact installed and I am using primereact/captcha.
Maybe I have misunderstood something, but isn't the following code supposed to work (console.log('Child component did update'))?
import React from 'react';
import { Captcha } from 'primereact/captcha';
export default function App() {
return (
<div className="App">
<ParentComponent/>
</div>
);
}
class Child extends React.Component {
componentDidUpdate () {
console.log('Child component did update');
}
render() {
return (<h2>Child component</h2>);
}
}
class ParentComponent extends React.Component {
constructor() {
super();
this.state = {
captchaSovled: false,
key : Math.random()
}
}
render() {
let output;
if (this.state.captchaSolved) {
output = <Child key={this.state.key} />;
} else {
output =<Captcha siteKey="xxxxxxx" onResponse={() => this.setState({ key : Math.random(), captchaSolved: true })} />
}
return (
<div>
<h1>Parent component</h1>
{output}
</div>
);
}
}
From React doc
componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.
In your code, the Child component is mounted after captchaSolved state is set, therefore only componentDidMount is fired on Child component.
componentDidUpdate is fired, if there is any change in the state or props. As of your component child:
class Child extends React.Component {
componentDidUpdate () {
console.log('Child component did update');
}
render() {
return (<h2>Child component</h2>);
}
}
There is no state or props which are changing, that's why componentDidUpdate never get's invoked.

Why is HOC with styles throwing an error?

I build a HOC and wanted to wrap it withStyles. But then I get the following error. What is wrong?
TypeError: Cannot call a class as a function
react js
const withLoader = (loadingProp) => (WrappedComponent) => {
return class LoadIndicator extends Component {
render() {
return <h1>hello world</h1>
}
}
}
export default withStyles(styles)(withLoader)
Functional components return should always be like render function return value is in class components.
Following modification to your code should fix your error.
class LoadIndicator extends Component {
render() {
return <h1>hello world</h1>
}
}
const withLoader = (loadingProp) => (WrappedComponent) => {
return <LoadIndicator />
}
export default withStyles(styles)(withLoader(loadingProp))
This loader HOC needs to be instantiated with the prop name of the loading flag.
I'm going to assume the styles HOC is for LoadIndicator:
const withLoader = (loadingProp) => (WrappedComponent) => {
class LoadIndicator extends Component {
render() {
// Todo: render WrappedComponent and/or a loading element.
return <h1>hello world</h1>
}
}
return withStyles(styles)(LoadIndicator);
}
export default withLoader;
Now when you use this HOC you still need to specify what the loading property is called:
withLoader('loading')(SomeComponent)
You should wrap the returned component instead of the HOC function:
const withLoader = (loadingProp) => (WrappedComponent) => {
return withStyles(styles)(class LoadIndicator extends Component {
render() {
return <h1>hello world</h1>
}
}}
}
export default (withLoader)

React function not being invoked, unless calling it directly

I am trying to generate a component dynamically and render the component as a return from a function.
import React, { Component } from 'react';
import HelloWorld from './helloWorld';
export default class BaseComponent extends Component {
constructor(props) {
super(props)
this.getFields = this.getFields.bind(this)
}
getFields() {
let temp = { hi: 'Hello', world: 'World!' }
const test = temp.map(function(val, i) {
return <HelloWorld key={i} val={val} />
})
return test
}
render() {
return (
<div className="container">
{this.getFields}
</div>
);
}
}
But this function is not being invoked unless I use {this.getFields()} in my render method. Why can't I just use {this.getFields}, as that is bound in the constructor and hence should render the value. What am I missing?
You need to call the method from render function to use the returned value and not assign it and hence you need {this.getFields()}
render() {
return (
<div className="container">
{this.getFields()}
</div>
);
}
You need to invoke the function:
{this.getFields()}

Why is the Object data not displayed in the browser when used inside JSX?

I have a React Container that is connected to Redux Store. My Redux Store has an array of data which i consume in mapStateToProps. But I am unable to use it inside JSX. There is no error. However, Nothing is displayed in the browser. Console logging the object gives the properties.
import React from 'react';
import {connect} from 'react-redux';
class CurrentStore extends React.Component {
render () {
console.log(this.props.current);
return (
<div className='centered row'>
<div className='column'>
{this.props.current.name}
</div>
</div>
);
}
}
function mapStateToProps (state, ownProps) {
return {
current: state.app.stores.filter(s => s._id === ownProps.match.params.storeId)
}
}
export default connect(mapStateToProps)(CurrentStore);
Array.prototype.filter gives you an array of values back, which means that your current object is in fact an array of objects.
So either, you should change your mapStateToProps to return the first value, like:
function mapStateToProps (state, ownProps) {
return {
current: state.app.stores.filter(s => s._id === ownProps.match.params.storeId)[0]
}
}
, or you should do it in the render function (which looks lots more appropriate)
class CurrentStore extends React.Component {
render () {
console.log(this.props.current);
return (
<div className='centered row'>
{ this.props.current && this.props.current.map( item => <div className='column'>{ item.name }</div>}
</div>
);
}
}
Inside the render it is now possible that more than 1 item would be returned, in case you don't want that, you could change the render to take only the first item
class CurrentStore extends React.Component {
render () {
let { current } = this.props;
if (!current || !current[0]) {
return null;
}
return (
<div className='centered row'>
<div className='column'>{ current[0].name }</div>
</div>
);
}
}

Resources