React is rendering [Object object] instead of JSX elements inside the array - javascript

I have this array of buttons in my React application:
const buttons = [
<Button label='Close' onClick={props.handleClose} />,
<Button label='Save' onClick={props.handleSubmit} />,
<Button label='Reset' onClick={props.handleReset} />
]
But when I render them:
render() {
return (
<div className='buttons'>{buttons.join()}</div>
)
}
I get:
Object object Object object Object object
And I want to get those elements.

There is no need to use .join(). It's enough to just pass an array like that:
render() {
return (
<div className='buttons'>{buttons}</div>
)
}

Why need to use join. There is no need.
const buttons = [
<Button label='Close' onClick={props.handleClose} />,
<Button label='Save' onClick={props.handleSubmit} />,
<Button label='Reset' onClick={props.handleReset} />
]
render() {
return (
<div className='buttons'>{buttons}</div>
)
}

Related

Wrong rendering when using map to render from an array in React

In this issue, I have an array of objects like:
const sampleCards = [
{userName:'user1',avatarURL:'../avatar/role11.jpg',mainPageURL:'/',isFollowed:true},
{userName:'user2',avatarURL:'../avatar/role12.jpg',mainPageURL:'/',isFollowed:true},
{userName:'user3',avatarURL:'../avatar/role13.jpg',mainPageURL:'/',isFollowed:false},
];
then I used map() to render this array:
export default function SearchBar(){
return (
<div className='searchBar'>
{sampleCards.map((result)=>{
return (
<SearchResultCard result={result}/>
);
})}
</div>
);
}
A SearchResultCard component be like:
export default function SearchResultCard(result){
const [isFollowed,setIsFollowed] = useState(result.isFollowed);
const handleFollowingClicked = ()=>{
setIsFollowed(false);
};
const handleFollowClicked=()=>{
setIsFollowed(true);
};
useEffect(()=>{console.log(result.userName)});
return (
<div className="search-result-card">
<Link to={result.mainPageURL}>
<div className="search-result-card-left">
<img src={result.avatarURL} alt={result.userName} className="search-result-img"/>
<p>{result.userName}</p>
</div>
</Link>
{isFollowed ? (
<button className="following-button" onClick={handleFollowingClicked}>Following</button>
):(
<button className="follow-button" onClick={handleFollowClicked}>Follow</button>
)}
</div>
);
}
The problem is that values in result seem not been passed to so it just ends with a 'Follow' button while other elements are not rendered
The parameter in SearchResultCard is the entire props object. This has a property called result. You need to destructure to get the result prop:
function SearchResultCard({ result }){ ... }
Here's a snippet:
const {useState} = React;
const sampleCards = [
{ userName:'user1', isFollowed:true },
{ userName:'user2', isFollowed:true },
{ userName:'user3', isFollowed:false },
];
function SearchResultCard({ result }){
const [isFollowed,setIsFollowed] = useState(result.isFollowed);
const handleFollowingClicked = _ => setIsFollowed(false);
const handleFollowClicked= _ => setIsFollowed(true);
return (
<div className="search-result-card">
{isFollowed
? ( <button onClick={handleFollowingClicked}>Following</button>)
: ( <button onClick={handleFollowClicked}>Follow</button>)
}
</div>
);
}
function Example(){
return (
<div className='searchBar'>
{sampleCards.map((result)=>{
return (
<SearchResultCard key={result.userName} result={result}/>
);
})}
</div>
);
}
// Render it
ReactDOM.createRoot(
document.getElementById("root")
).render(
<Example />
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
<div id="root"></div>

React - dynamic properties from strings

In my test suit, I am trying to generate components with props dynamically, so I will end up with components like so:
<Button primary />
<Button secondary />
Currently, I am a bit stuck:
[
'primary',
'secondary'
].forEach((buttonType) => {
it(`should render the '${buttonType}' button`, () => {
const button = mount(<Button {...buttonType}>Click me</Button>); // incorrect - will not work
// rest of the test omitted
});
});
Any help would be appreciated.
You should replace cardType with buttonType in your function parameter given to forEach.
Then, you should use the following inside the test:
const dynamicProps = { [buttonType]: true };
<Button {...dynamicProps} />
Some elements have been omitted but you get the idea. When you pass a prop without an explicit definition, you actually mean someProp={true}, so in the above case you have to use the primary or whatever as the property of an object, with a value of true.
You cannot map through an array of strings an apply those as boolean props the way you're doing it. Try instead iterating through a map that has each of your button types as keys with true values.
class MyApp extends React.Component {
render() {
let arr = [{primary: true}, {secondary: true}]
return (
<div>
{arr.map(buttonType => <Button {...buttonType} />)}
</div>
);
}
}
const Button = (props) => {
if(props.primary) return <button className="primary">Primary</button>;
if(props.secondary) return <button className="secondary">Secondary</button>;
return null;
}
ReactDOM.render(<MyApp />, document.getElementById("app"));
.primary {
background: orange;
}
.secondary {
background: grey;
}
<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>
<div id="app"></div>

How to render array of object in ReactJS

I want to get list of users using reactJS 15.5, Code:
constructor(props) {
super(props);
this.state = {
values: []
};
this.getDataFromUser = this.getDataFromUser.bind(this);
}
render() {
return (
<div>
<button onClick={this.getDataFromUser}>Fetch</button>
<p>
{this.state.values}
</p>
</div>
);
}
getDataFromUser(){
fetch('http://localhost:8000/api/user')
.then(response => response.json())
.then(json => {
console.log(json);
this.setState({values: json })
})
}
In console.log(json), i get
But i get this error when i click in button Fetch so error in getDataFromUser:
Unhandled Rejection (Invariant Violation): Objects are not valid as a
React child (found: object with keys {id, nom, prenom,
email,is_admin,created_at, updated_at}). 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.
There's a why to create user object, So anyone can help me to resolve this issue and thanks
Its an array of objects so you need to use map to iterate the array then render the specific values.
Like this:
render() {
return (
<div>
<button onClick={this.getDataFromUser}>Fetch</button>
<p>
{this.state.values.map(el => {
return <div key={el.id}>
<span>{el.email}</span>
<span>{el.nom}</span>
<span>{el.is_manager}</span>
</div>
})}
</p>
</div>
);
}
You are trying to return an invalid object/array in your react component. In your case you should try and iterate through the array(this.state.values) and render the items(string values) needed.
render() {
const { values } = this.state;
return (
<div>
<button onClick={this.getDataFromUser}>Fetch</button>
<p>
{values.map((value, index) =>
(<div key={index}>{value.nom}</div>)
)}
</p>
</div>
);
}
Looking at the error it looks like the new state of this.state.values is rather an object with the following keys {id, nom, prenom, email,is_admin,created_at, updated_at}. So the below code would work for u.
render() {
const { values } = this.state;
return (
<div>
<button onClick={this.getDataFromUser}>Fetch</button>
<p>
{values &&
<div>
{values.nom}
{values.prenom}
{values.email}
etc.
</div>
}
</p>
</div>
);
}

lodash map get index key got unexpected token

import { map } from 'lodash';
render(){
return(
{map(new_applicants, (obj,index) =>
<div key={index}>{index}</div>
)}
)
}
What's wrong with this code? obj is the single array of object been iterate and index is the key. I'm using lodash. The error look like this in console.
{map(new_applicants, (obj,index) =>
| ^
The problem is that the {...} syntax is being taken for an object initializer; you're doing this outside of JSX. That syntax is only valid within a JSX section, e.g.
<div>{map(...)}</div>
Also, render has to return a component (or null), it can't return an array. So perhaps:
return(
<div>
{map(new_applicants, (obj,index) =>
<div key={index}>{index}</div>
)}
</div>
)
Example:
const map = _.map;
class Foo extends React.Component {
render() {
const new_applicants = [1, 2, 3];
return(
<div>
{map(new_applicants, (obj,index) =>
<div key={index}>{index}</div>
)}
</div>
)
}
}
ReactDOM.render(
<Foo />,
document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
Write it like this, {} it required when you are running the js code inside html element:
render(){
return(
<div>
{
map(new_applicants, (obj,index) =>
<div key={index}>{index}</div>
)
}
</div>
)
}

Return multiple React elements in a method without a wrapper element

I'm trying to return multiple React elements from a helper method. I could solve it simply by moving around some code, but I'm wondering if there's a cleaner way to solve it. I have a method that returns part of the render method, and that functions needs to return both a React element and some text. It's clearer through an example:
class Foo extends React.Component {
_renderAuthor() {
if (!this.props.author) {
return null;
}
return [
' by ',
<a href={getAuthorUrl(this.props.author)}>{this.props.author}</a>,
]; // Triggers warning: Each child in an array or iterator should have a unique "key" prop.
render() {
return (
<div>
{this.props.title}
{this._renderAuthor()}
</div>
);
}
}
I know the render method has to return exactly 1 React element. Using a helper method like this would trigger a warning, and fixing the warning (by adding keys) would make the code too convoluted. Is there a clean way to do this without triggering a warning?
Edit:
Another use case:
render() {
return (
<div>
{user
? <h2>{user.name}</h2>
<p>{user.info}</p>
: <p>User not found</p>}
</div>
);
}
Edit 2:
Turns out this isn't possible yet, I wrote about 2 workarounds here: https://www.wptutor.io/web/js/react-multiple-elements-without-wrapper
Support has been added using the Fragment component. This is a first-class component.
So you can now use:
render() {
return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
);
}
For more information visit: https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html
The error message tells you exactly how to solve this:
Each child in an array or iterator should have a unique "key" prop.
Instead of this:
return [
' by ',
<a href={getAuthorUrl(this.props.author)}>{this.props.author}</a>,
];
Do this:
return [
<span key="by"> by </span>,
<a key="author" href={getAuthorUrl(this.props.author)}>{this.props.author}</a>,
];
Yes, you need to wrap the text node ("by") in a span in order to give it a key. Such are the breaks. As you can see, I've just given each element a static key, since there's nothing dynamic about them. You could just as well use key="1" and key="2" if you wanted.
Alternatively, you could do this:
return <span> by <a href={getAuthorUrl(this.props.author)}>{this.props.author}</a></span>;
...which obviates the need for keys.
Here's the former solution in a working snippet:
const getAuthorUrl = author => `/${author.toLowerCase()}`;
class Foo extends React.Component {
_renderAuthor() {
if (!this.props.author) {
return null;
}
return [
<span key="by"> by </span>,
<a key="author" href={getAuthorUrl(this.props.author)}>{this.props.author}</a>,
];
}
render() {
return (
<div>
{this.props.datePosted}
{this._renderAuthor()}
</div>
);
}
}
ReactDOM.render(<Foo datePosted="Today" author="Me"/>, document.getElementById('container'));
<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>
<div id="container"></div>
It's not currently possible to do this without some sort of workaround like wrapping everything in another component, since it ends up with the underlying React code trying to return multiple objects.
See this active Github issue where support for this is being considered for a future version though.
Edit: You can now do this with Fragments in React 16, see:
https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html
There is another way to solve this. I will suggest you create another component Author.js:
function Author(props) {
return (<span>
<span> by </span>
<a href={props.getAuthorUrl(props.author)}>{props.author}</a>
</span>)
}
class Foo extends React.Component {
render() {
return (
<div>
{this.props.title}
{this.props.author && <Author author={this.props.author} getAuthorUrl={this.getAuthorUrl} />}
</div>
);
}
}
I didn't test this code though. But it will look more cleaner I think. Hope it helps.
I like to have an If-component around for such things, and I have wrapped everything into a span, as it doesn't really break anything and makes the need for keys go away...
const getAuthorUrl = author => `/${author.toLowerCase()}`;
function If({condition,children}) {
return condition ? React.Children.only(children) : null;
}
class Foo extends React.Component {
render() {
return (
<div>
{this.props.datePosted}
<If condition={this.props.author}>
<span> by
<a key="author" href={getAuthorUrl(this.props.author)}>
{this.props.author}
</a>
</span>
</If>
</div>
);
}
}
ReactDOM.render(<Foo datePosted="Today" author="Me"/>, document.getElementById('container'));
<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>
<div id="container"></div>
...skipping the array thing altogether?
This is a bit hacky but it doesn't have unnecessary jsx as you wished.
var author = 'Daniel';
var title = 'Hello';
var Hello = React.createClass({
_renderAutho0r: function() {
if (!author) {
return null;
}
return {author}
},
render: function() {
var by = author ? ' by ' : null;
return (
<div>
{title}
{by}
{this._renderAutho0r()}
</div>
);
}
});
React.render(<Hello name="World" />, document.body);
my JSFiddle
You can return fragments from sub-rendering functions but not from the main render function, at least before React 16. In order to do so, return an array of components. You don't need to set keys manually unless your fragment children will change (arrays are keyed with indices by default).
For creating fragments you may also use createFragment.
For inline usage, you may use an array or leverage immediately invoked arrow function.
See the example below:
const getAuthorUrl = author => `/${author.toLowerCase()}`;
class Foo extends React.Component {
constructor() {
super();
this._renderAuthor = this._renderAuthor.bind(this);
this._renderUser = this._renderUser.bind(this);
}
_renderAuthor() {
if (!this.props.author) {
return null;
}
return [
' by ',
<a href={getAuthorUrl(this.props.author)}>{this.props.author}</a>,
];
}
_renderUser() {
return [
<h2>{this.props.user.name}</h2>,
<p>{this.props.user.info}</p>
]
}
render() {
return (
<div>
{this.props.datePosted}
{this._renderAuthor()}
<div>
{this.props.user
? this._renderUser()
: <p>User not found</p>}
</div>
<div>
{this.props.user
? [
<h2>{this.props.user.name}</h2>,
<p>{this.props.user.info}</p>
]
: <p>User not found</p>}
</div>
<div>
{this.props.user
? (() => [
<h2>{this.props.user.name}</h2>,
<p>{this.props.user.info}</p>
])()
: <p>User not found</p>}
</div>
</div>
);
}
}
ReactDOM.render(<Foo datePosted="Today" author="Me" user={{name: 'test', info: 'info'}} />, document.getElementById('container'));
<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>
<div id="container"></div>
In order to not get warnings each child must be assigned a key. In order to do so, instead of returning an array please use helper function fragment(...children) to assign index-based keys automatically. Please note that strings must be converted to spans or other nodes that can be assigned with a key:
const fragment = (...children) =>
children.map((child, index) =>
React.cloneElement(
typeof child === 'string'
? <span>{child}</span>
: child
, { key: index }
)
)
const getAuthorUrl = author => `/${author.toLowerCase()}`;
const fragment = (...children) =>
children.map((child, index) =>
React.cloneElement(
typeof child === 'string'
? <span>{child}</span>
: child
, { key: index }
)
)
class Foo extends React.Component {
constructor() {
super();
this._renderAuthor = this._renderAuthor.bind(this);
this._renderUser = this._renderUser.bind(this);
}
_renderAuthor() {
if (!this.props.author) {
return null;
}
return fragment(
' by ',
<a href={getAuthorUrl(this.props.author)}>{this.props.author}</a>
);
}
_renderUser() {
return fragment(
<h2>{this.props.user.name}</h2>,
<p>{this.props.user.info}</p>
)
}
render() {
return (
<div>
{this.props.datePosted}
{this._renderAuthor()}
<div>
{this.props.user
? this._renderUser()
: <p>User not found</p>}
</div>
<div>
{this.props.user
? fragment(
<h2>{this.props.user.name}</h2>,
<p>{this.props.user.info}</p>
)
: <p>User not found</p>}
</div>
<div>
{this.props.user
? (() => fragment(
<h2>{this.props.user.name}</h2>,
<p>{this.props.user.info}</p>
))()
: <p>User not found</p>}
</div>
</div>
);
}
}
ReactDOM.render(<Foo datePosted="Today" author="Me" user={{name: 'test', info: 'info'}} />, document.getElementById('container'));
<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>
<div id="container"></div>
Try this:
class Foo extends React.Component {
_renderAuthor() {
return <a href={getAuthorUrl(this.props.author)}>{this.props.author}</a>
}
render() {
return (
<div>
{this.props.title}
{this.props.author && " by "}
{this.props.author && this._renderAuthor()}
</div>
);
}
}
Perhaps a more simple way would be to rethink how you're architecting your application. However, in a more simple way.
You're triggering the warning because you're trying to render from an array and not react elements but directly html. In order to approach this, you would have to do
{this._renderAuthor().map(
(k,i) => (React.addons.createFragment({k}))
) }
React addons createFragment function basically does that, it reduces your html elements into react fragments that you can render.
React createFragment documentation
Alternatively, in a much better approach, you can create an AuthorLink stateless component like this..
function AuthorLink(props) {
return (
<div className="author-link">
<span> by </span>
<a href={props.authorUrl}> {props.author} </a>
</div>
});
}
and use this in your main component's render
render() {
const { author } = this.props;
return (
<div>
{this.props.datePosted}
<AuthorLink url={getAuthorUrl(author)} author={author} />
</div>
);
}
Try this approach on your array:
return [
<span key={'prefix-'+random_string_generator()}>' by '</span>,
<a key={'prefix-'+random_string_generator()} href={getAuthorUrl(this.props.author)}>{this.props.author}</a>,
];

Categories