ReactJS - Listing all keys at once - reactjs

I'm a beginner of ReactJS and I'm stuck trying to figure out why map only returns a single prop at a time.
In file1.jsx, I have an array that contains 3 objects:
var MainPanelOneData = [{"id":1,"shotviews":15080},{"id":2,"likes":12000},{"id":3,"comments":5100}];
File1.jsx also has a render function to extrapolate the data inside the array:
render: function() {
var ListMainPanelOne = MainPanelOneData.map(function(data) {
return <MainPanelOne key={data.key} shotviews={data.shotviews} likes={data.likes} comments={data.comments} />
});
In file2.jsx, I have this code to render the data object from file1.jsx:
render: function() {
return (
<div>
<span>{this.props.shotviews} shot views</span>
<span>{this.props.likes} likes</span>
<span>{this.props.comments} comments</span>
</div>
)
}
The result shows this:
15080 shot views likes comments
shot views12000 likes comments
shot views likes5100 comments
I'm guessing it repeats like this because it searches through one key at a time? If that's the case, how do I display all keys at the same time? Use indexing?

well your array of data doesnt have all the keys. each one of your objects in PanelOneData has ONE key and is missing the other two; additionally none of them have key called key. so youre making three MainPanelOne components, each with a single prop. the result of that map is this
[
<MainPanelOne key={null} shotviews={15080} likes={null} comments={null} />,
<MainPanelOne key={null} shotviews={null} likes={12000} comments={null} />,
<MainPanelOne key={null} shotviews={null} likes={null} comments={5100} />
]
which is an accurate display of what youre seeing

To get one line you might do something like this.
render: function() {
var ListMainPanelOne = MainPanelOneData.map(function(data) {
return <span key={data.id}> {data.shotviews} {data.likes} {data.comments} </span>
});

Related

Remove duplicate JSX elements | React Strapi graphql

Long story short, i have and array of JSX elements, that looks something like this:
<ChatListCard
key={index}
prop1={prop1}
prop2={prop2}
prop3={prop3}
/>
I get the props for these "Cards" from two different tables in Strapi/Graphql API, so I do something like this:
[].concat(
array1.map((item,index)=> <Card key={index} prop1={item.prop1} ... />),
array2.map((item,index)=> <Card key={index} prop1={item.prop1} ... />)
)
The problem is that array1 and array2 contain some "items" that are identical, and need to be filtered out. Is there a way to do it, using JS:
[].concat(...).filter((magic)=> magic but filtered) //use the filter here
, or i should do it in GraphQL.
(I have already used where clause in there to filter out only the items that I do not need)
query ProposalsAndRequests($input:String!){
proposals(where: {_or:[
{owner:{email:$input}}
{task:{owner:{email:$input}}}
]},sort:"created_at:desc"){
id
...
}
}
chatRequests(where:{_or:[
{users_permissions_user:{email:$input}}
{task:{owner:{email:$input}}}
]},sort:"created_at:desc"){
id
...
}
}
note: chatRequests and Proposals contain identical fields, they just serve different purposes elsewhere in the site
the users_permissions_user and owner are also the same relation
You can do it using the "Set" datastructure in js. const set = new Set(arr);. Sets cant have duplicates! :-) but they can have identical objects if the references are not the same.
For a more complex filter, use .reduce function to accumilate only uniques.
Or, you could remove the duplicates by bruteforce it with something like:
const noDubs = [];
myArr.foreach(item => {
if(!noDubs.some(entry = entry.special.property === item.special.property) noDubs.push(item);
});

How to render dynamic components onClick

My code creates dynamic components/elements based on size of list of entities (in cache/memory). How can I modify this so that the onClick on line 32 (fab-click) adds yes another entity to the list. I ommitted the MainMenuElement class so lets assume it works. I think this is a problem of not knowing how to "think in react". Must I use react's state to achieve this, or is there a cleaner way?
I am actually adapting this from an HTML5/CSS3 app which used and am finding this to be much harder than just appending children from anywhere/any-time like with templates. Help.
createMainMenuElement(conversation){
return <MainMenuElement conversation = {conversation} key ={conversation.key} />
}
createMainMenuElements(conversations) {
return conversations.map(this.createMainMenuElement)
}
generateData = function(){
let usernames = ["tony","john","doe","test", "bruce"]
let data = [];
for(let i = 0; i < 5; i++){
let temp = {key: i.toString(), username: usernames[i], timestamp: "4:30", subtitle: "The quick brown fox jumps over the lazy dog"}
data.push(temp)
this.mainMenuStack+=1;
}
return data;
};
handleFabClick() {
console.log("test")
let temp = {key:this.mainMenuStack.toString(), username: "Baby", timestamp: "12:30", subtitle: "The quick red cat jumps over the yellow dog"};
this.createMainMenuElement(temp);
};
render(){
return(
<div className={cx('mainMenu')}>
<div className={cx('mainMenu__element','mainMenu__element--pseudo')} onClick={this.handleFabClick}>
<div className={cx('mainMenu__element__icon fab')} id="fab">
<div className={cx('fab__icon')}>+</div>
</div>
<div className={cx('mainMenu__element__textWrapper')}>
<div className={cx('mainMenu__element__title')}>New Conversation</div>
</div>
</div>
{this.createMainMenuElements(this.generateData())} //WORKS ON LOAD
//WANT TO RENDER/APPEND DYNAMIC COMPONENTS HERE
</div>
)
};
}
You're thinking about the DOM, when you need to think about the data. In React, the DOM is purely a function of your data.
You'll need to store the dynamically created data, let's use an array
constructor(props){
super(props)
this.state = {
elements:[]
}
}
Then render the data. elements is empty for now, that's fine. But we know that eventually, the user will create data dynamically. The render function already handles that!
render(){
return (
<div>
//other code
{this.state.elements.map(this.createMainMenuElement)}
</div>
)
}
Now let's add the data.
handleFabClick() {
let temp = {key:this.mainMenuStack.toString(), username: "Baby", timestamp: "12:30", subtitle: "The quick red cat jumps over the yellow dog"};
this.setState({
elements: [...this.state.elements, temp]
})
};
We've now changed the state of the component, which causes it to rerender, which will display the new data. No DOM operations needed!
My code does not translate directly to your question, i'm merely showing you the React fundamentals. You said that you want to add elements to an existing list, so it looks like elements needs to contain ["tony","john","doe","test", "bruce"] by default. I hope you get the point though.

Accessing state of children from parent component?

I don't think I fully understand how the Parent/Child relationship works in React. I have two components, Column and Space. When a Column is rendered, it creates some Spaces. I thought that meant that the Column would be the parent to those Spaces, but either I'm wrong or I'm using some part of React.Children.count(this.props.children) incorrectly - it always tells me that any given Column has 0 children.
Simplified models:
var Column = React.createClass({
getInitialState: function() {
return {childCount: '', magicNumber: this.props.magicNumber};
},
componentDidMount: function() {
var newCount = React.Children.count(this.props.children);
this.setState({childCount: newCount});
},
render: function() {
return (
<div className='Column'>
{
Array.from({ length: this.state.magicNumber }, (x,y) => <Space key={y}/>)
}
</div>
);
}
});
var Space = React.createClass({
render: function() {
return (
<div className="Space">
space here
</div>
);
}
});
It seems like no matter where I put React.children.count(this.props.children) in a Column, it tells me there are 0 children. I would expect the Column generated in the example code to have five children, since five Spaces are created within it.
I figured maybe I was trying to count the Children before they were fully loaded, so I tried writing a click handler like this:
//in Column
setChildCount: function () {
var newCount = React.Children.count(this.props.children);
this.setState({childCount: newCount});
}
Then adding a button like this in my Column:
...
render: function() {
return (
<div className='Column'>
{
Array.from({ length: this.state.magicNumber }, (x,y) => <Space key={y}/>)
}
<button onClick={this.setChildCount} />
{this.state.childCount}
</div>
);
But my childCount is always and forever 0. Can anyone see where I'm going wrong?
Edit: Ultimately, I want to get a count of all children elements who have X attribute in their state set to Y value, but I am clearly a step or three away from that.
The Column component doesn't have any children on that code. Childrens are components which are wrapped by the parent component. So imagine:
<Column>
<Space/>
<Space/>
<Column/>
In this case the parent Column has two children Space
On your code:
<div className='Column'>
{
Array.from({ length: this.state.magicNumber }, (x,y) => <Space key={y}/>)
}
</div>
You are creating new components inside a divnot inside the component Column
You are rendering Space components as part of the Column component. The parent/child relationship captured by this.props.children looks like this:
var Column = React.createClass({
render: function() {
<div className="column">
{this.props.children}
</div>
}
});
ReactDOM.render(
<Column>
<Space /> //little space children
<Space />
</Column>
);
To get at your specific problem, you don't need anything like this.props.children because you have everything right there in your render method.
So the answer to your question is: apply the same logic as when you render them.

ReactJS parent/child list items not rendering properly after an item is removed

Example: https://jsfiddle.net/wbellman/ghuw2ers/6/
In an application I am working on, I have a parent container (List, in my example) that contains a list of children (Hero, in my example). The list is governed by an outside object. For simplicity I declared the object directly in the JS. (In my real application the data store is properly namespaced and so forth.)
The problem I have is in the list I have three elements, if I remove an item from the middle, the rendered list appears to remove the last element. However the outside object reflects the proper list.
For example:
My list has the elements: cap, thor, hulk
If you remove "thor", "cap" and "thor" are rendered
The heroList reflects "cap" and "hulk" as it should
I am relatively new to ReactJs, so there is a good chance my premise is fundamentally flawed.
Note: The example reflects a much more complex application. It's structured similarly for purposes of demonstration. I am aware you could make a single component, but it would not be practical in the actual app.
Any help would be appreciated.
Here is the code from JSFiddle:
var heroList = [
{ name: "cap" },
{ name: "thor"},
{ name: "hulk"}
];
var List = React.createClass({
getInitialState() {
console.log("heros", heroList);
return {
heros: heroList
};
},
onChange(e){
this.setState({heros: heroList});
},
removeHero(i,heros){
var hero = heros[i];
console.log("removing hero...", hero);
heroList = _.filter(heroList, function(h){ return h.name !== hero.name;});
this.setState({heros:heroList});
},
render() {
var heros = this.state.heros;
var createHero = (hero,index) => {
return <Hero hero={hero} key={index} onRemove={this.removeHero.bind(this,index,heros)}/>;
};
console.log("list", heros);
return (
<ul>
{heros.map(createHero)}
</ul>
)
}
});
var Hero = React.createClass({
getInitialState() {
return {
hero: this.props.hero
}
},
render() {
var hero = this.state.hero;
return (
<li>Hello {hero.name} | <button type="button" onClick={this.props.onRemove}>-</button></li>
);
}
});
ReactDOM.render(
<List />,
document.getElementById('container')
);
Additional: I was having problems copying the code from JSFiddle, anything I broke by accident should work in the JSFiddle listed at the top of this question.
Edit:
Based on the commentary from madox2, nicole, nuway and Damien Leroux, here's what I ended up doing:
https://jsfiddle.net/wbellman/ghuw2ers/10/
I wish there was a way to give everyone credit, you were all a big help.
Changing your Hero class to this fixed the issue of displaying the wrong hero name for me:
var Hero = React.createClass({
render() {
return (
<li>Hello {this.props.hero.name} | <button type="button" onClick={this.props.onRemove}>-</button></li>
);
}
});
i.e. I removed the local state from the class and used the prop directly.
Generally speaking, try to use the local store only when you really need it. Try to think of your components as stateless, i.e. they get something through the props and display it, that's it.
Along these lines, you should consider passing the hero list through the props to your List component as well.
if you really have problems with managing your data you should use Flux or Redux.
in this code:
heroList = _.filter(heroList, function(h){ return h.name !== hero.name;});
i just dont get why you filer the heroList instead of this.state.heros? every time you add or remove a hero, the heroList in your current scope shouldnt be kept in state? the global heroList is just the initial state.
The problem is with the keys used. Since the key is taken from the index, that key has already been used and thus the hero with that key is shown.
change it to key={Math.random() * 100} and it will work

React JSX form field map fails if accessing certain properties

I'm attempting to build a form from an array of form fields where each form field looks like this:
{
"name": "state",
"resource": "customer",
"type": "TextBox",
"assetId": "State",
"label": {
"text": "State",
"assetId": "Label"
}
}
However, when I attempt to map it using JSX, the fields don't get successfully displayed if I access certain properties of the object. Take the following code, which functions correctly:
formfields.map(function (formfield, i) {
var returnfield = <div key={i}>{formfield.name}</div>;
switch (formfield.type) {
case "TextBox":
console.log(formfield.label);
returnfield = (
<div key={i}>
<label htmlFor="theinput">{formfield.name}</label>
<input id="theinput" type="text" value={formfield.name} />
</div>
);
break;
}
return returnfield;
});
And compare it with the code that fails:
formfields.map(function (formfield, i) {
var returnfield = <div key={i}>{formfield.name}</div>;
switch (formfield.type) {
case "TextBox":
console.log(formfield.label.text);
returnfield = (
<div key={i}>
<label htmlFor="theinput">{formfield.name}</label>
<input id="theinput" type="text" value={formfield.name} />
</div>
);
break;
}
return returnfield;
});
The astute observer will notice that the only difference between the two is that, in the second, we are logging formfield.label.text instead of formfield.label
I'm totally stumped why simply logging an object's grandchild attribute should cause the form to appear empty (i.e., with no fields). Perhaps I'm running into reserved names or something? Any ideas appreciated.
why didn't I see a javascript error in my developer console? Is there some weird thing where .map() doesn't allow errors to be raised?
After recognizing that checking for null is needed in your project well I suggest you use some concepts of javascript functional programming to compose a function that checks for falsely values before applying them in your logic.
You can use Maybe functor that returns a Maybe(null) which stops immediately. Before returning a null value to your logic and cause a boom!
You can also use Either, this is cool because it's just like maybe but you can also gve some logic to run if the value is falsely.
I have two examples for these suggestions (Copied from jsbin)
//Key container == Something map can iterate over like an object or an array.
//And am talking about the lodash / ramda.js curried map that can iterate over object not the js native one.
//Using Maybe
//Url http://jsbin.com/yumog/edit?js,console
var safeGet = _.curry(function(x,o){
return Maybe(o[x]);
//This will return Maybe(null)
//if it's some property in a container is not found
//which you can check before breaking something
});
var user = {id: 2, name: "Albert"}
var ex3 = compose(map(_.head), safeGet('name'));
assertDeepEqual(Maybe('A'), ex3(user))
console.log("exercise 3...ok!")
//Using Either.io
//url http://output.jsbin.com/bexuc/
// Write a function that uses checkActive()
//and showWelcome() to grant access or return the error
var showWelcome = compose(_.add( "Welcome "), _.get('name'))
//Here either returns a function you give it on the right if it's truthy
//and left if it's falsey (or falsy i don't know english .. )
// So you get to do something if the property in your container is not present.
var checkActive = function(user) {
return user.active ? Right(user) : Left('Your account is not active')
}
var ex1 = compose(map(showWelcome), checkActive);
assertDeepEqual(Left('Your account is not active'), ex1({active: false, name: 'Gary'}))
assertDeepEqual(Right('Welcome Theresa'), ex1({active: true, name: 'Theresa'}))
Links to the libraries.
Maybe: https://github.com/chrissrogers/maybe
Either: https://github.com/fantasyland/fantasy-eithers
You might also need to check on lodash / ramda to have a full idea on these functional concepts.

Resources