I'm learning react and I'm stuck on how to render the birthdays within my this.state. I figured I would use something like:
{this.state.birthdays}
but that doesn't seem to reach each birthday. My getElementByID is equal to a container which exists on my HTML. Any advice/help would be great!
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
birthdays: {
'January': [{
name: 'Mike',
date: '1/14/90'
}, {
name: 'Joe',
date: '1/7/92'
}],
March: [{
name: 'Mary',
date: '3/7/88'
}]
}
}
}
render() {
return (
<div>
</div>
);
}
}
ReactDOM.render(<App />,
document.getElementById('container'));
Try this:
{ Object.keys(this.state.birthdays).map(this.renderBirthdays) }
And then above your render function create a function called renderBirthdays like this:
renderBirthdays: function(key) {
return (
<div key={key} index={key} details={this.state.birthdays[key]}>
{details.name} - {details.date}
</div>
)
},
render: function() {
return (
<div>{ Object.keys(this.state.birthdays).map(this.renderBirthdays) }</div>
)
}
So you can take advantage of javascripts map which will take your object and key them. Then we're going to pass this key into a function called renderBirthdays which will iterate over the item. We need to pass a key and an index into the element, and for ease of use, we can pass a details prop into it equal to the currently selected item it's iterating over. That way we can just use {details.name} etc in the element.
This is untested, but something like this should work. Loop over the month keys using Object.keys, then reduce each set of birthdays to a flat array:
render() {
return (
<div>
{Object.keys(this.state.birthdays).reduce((birthdays, month) => {
return birthdays.concat(this.state.birthdays[month].map((bday) => {
return (
<p>{bday.name} - {bday.date}</p>
);
}));
}, [])}
</div>
);
}
Related
I would like to check if the value of my element appears in
an array if it is the case I give it the class
another class.
this is to make a map from another object array
How to check in the whole table if
Is there the value I want?
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
class Dbz extends React.Component {
constructor(props) {
super(props)
This.state = {
sayien = ['goku','vegeta','broly']
warrioz = [
{ name:goku
power: 1500
},
{ name: yamcha
power: 150
},
{ name: cell
power: 2500
},
]
}
}
render(){
return(
<div>
{ this.state.warrioz.map((data) => {
return (
<div className={this.state.sayien === data.name ? "sayien" :"nosayien"}>
<p>{data.name} </p>
</div>
})}
</div>
)
}
}
export default Dbz
You can see if data.name is included in your state. But the question remains, where is data coming from?
<div className={this.state.sayien.includes(data.name) ? "sayien" :"nosayien"}>
<p>goku </p>
</div>
If you are curious here is a link to the mdn doc for includes.
I have a header component where I need to render three buttons, so every three buttons have three props. One is the class name, click handler and text.
So out of three buttons, two buttons act as a toggle button, so based on the click the text should change.
See the below code:
class App extends Component(){
state = {
navigationList: [{
text: 'Signout',
onClickHandler: this.signoutHandler,
customClassName: 'buttonStyle'
}, {
text: this.state.isStudents ? 'Students' : 'Teachers',
onClickHandler: this.viewMode,
customClassName: 'buttonStyle'
}, {
text: this.state.activeWay ? 'Active On' : 'Active Hidden',
onClickHandler: this.activeWay,
customClassName: 'buttonStyle'
}]
}
signoutHandler = () => {
// some functionality
}
viewMode = () => {
this.setState({
isStudents: !this.state.isStudents
})
}
activeWay = () => {
this.setState({
activeWay: !this.state.activeWay
})
}
render(){
return (
<Header navigationList={this.state.navigationList}/>
)
}
}
const Header = ({navigationList}) => {
return (
<>
{navigationList && navigationList.map(({text, onClickHandler, customClassName}) => {
return(
<button
onClick={onClickHandler}
className={customClassName}
>
{text}
</button>
)
})}
</>
)
}
The other way is I can pass all the props one by one and instead of an array I can write three button elements render it, but I am thinking to have an array and render using a map.
So which method is better, the problem that I am facing is if use the array. map render
the approach I need to set the initial value as a variable outside and how can I set the state.
And I am getting the onClick method is undefined, is it because the function is not attached to the state navigation list array.
Update
I declared the functions above the state so it was able to call the function.
So in JS, before the state is declared in the memory the functions should be hoisted isn't.
class App extends React.Component {
constructor(props){
super();
this.state = {
isStudents:false,
activeWay:false,
}
}
createList(){
return [{
text: 'Signout',
onClickHandler: this.signoutHandler.bind(this),
customClassName: 'buttonStyle'
}, {
text: this.state.isStudents ? 'Students' : 'Teachers',
onClickHandler: this.viewMode.bind(this),
customClassName: 'buttonStyle'
}, {
text: this.state.activeWay ? 'Active On' : 'Active Hidden',
onClickHandler: this.activeWay.bind(this),
customClassName: 'buttonStyle'
}];
}
signoutHandler(){
}
viewMode(){
this.setState({
isStudents: !this.state.isStudents
})
}
activeWay(){
this.setState({
activeWay: !this.state.activeWay
})
}
render(){
return (
<div>
<div>ddd</div>
<Header navigationList={this.createList()} />
</div>
)
}
}
const Header = ({navigationList}) => {
console.log(navigationList);
return (
<div>
{navigationList && navigationList.map(({text, onClickHandler, customClassName}) => {
return(
<button
onClick={onClickHandler}
className={customClassName}
>
{text}
</button>
)
})}
</div>
)
}
ReactDOM.render(<App />, document.querySelector("#app"))
https://jsfiddle.net/luk17/en9h1bpr/
Ok I will try to explain, If you see you are using function expressions in your class and as far as hoisting is concerned in JavaScript, functions expressions are not hoisted in JS only function declarations are hoisted, function expressions are treated as variables in JS.
Now for your case you don't have to shift your functions above the state, you can simply use constructor for initializing state as
constructor(props) {
super(props);
this.state = {
isStudents: false,
activeWay: false,
navigationList: [
{
text: "Signout",
onClickHandler: this.signoutHandler,
customClassName: "buttonStyle"
},
{
text: "Teachers",
onClickHandler: this.viewMode,
customClassName: "buttonStyle"
},
{
text: "Active Hidden",
onClickHandler: this.activeWay,
customClassName: "buttonStyle"
}
]
};
}
Now you will have your handlers available as it is
Sandbox with some modification just to show
EDIT:
You can have default text for buttons and change it when clicking,
Sandbox updated
Hope it helps
I have a data structure like this {key: [array of object]}. I want to render each element in array of object using nested for loop like this:
for each entry(k, v) in map:
for each element in array v:
display html data
I am using react version 16.
I tried this in JSX:
class Positions extends React.Component {
renderPosition(position) {
var expiry = position["ExpiryDay"] + "-" + position["ExpiryMonth"] + "-" + position["ExpiryYear"];
console.log(expiry);
return (<label>{expiry}</label>);
}
render() {
return (
<div>
{this.props.positionsGrouped.forEach(function(positions) {
return (
<div>
{positions.map(function(position) {
return (
<div>
{this.renderPosition(position)}
</div>
);
}.bind(this))}
</div>
);
}.bind(this))}
</div>
);
}
}
Here is the JS that it compiles to:
class Positions extends React.Component {
renderPosition(position) {
var expiry = position["ExpiryDay"] + "-" + position["ExpiryMonth"] + "-" + position["ExpiryYear"];
console.log(expiry);
return React.createElement(
"label",
null,
expiry
);
}
render() {
return React.createElement(
"div",
null,
this.props.positionsGrouped.forEach(function (positions) {
return React.createElement(
"div",
null,
positions.map(function (position) {
return React.createElement(
"div",
null,
this.renderPosition(position)
);
}.bind(this))
);
}.bind(this))
);
}
}
However I don't see anything being rendered except for the top most div. Here is the rendered html:
<div id="app">
<div></div>
</div>
Here is what I see in react developer tools:
<App>
<Positions>
<div></div>
</Positions>
</App>
I don't see any errors in the console. I expected at least three nested divs to be rendered however I only see one so it sounds like something is wrong at the level of the first for loop. But, I do see my expiry variable being printed to console properly so I know renderPosition is getting called with the correct data.
Does anyone know what I am doing wrong? I'm new to react and sorry for any typos. Thanks in advance.
this.props.positionsGrouped.forEach would return undefined. I mean it wouldn't return anything. So nothing gets rendered.
Just change your component code like this
import React from "react";
class Positions extends React.Component {
constructor(props) {
super(props);
this.renderPosition = this.renderPosition.bind(this);
}
renderPosition(position) {
var expiry = position["name"] + "-" + position["title"];
console.log(expiry);
return <label>{expiry}</label>;
}
render() {
const { positionsGrouped } = this.props;
return (
<div>
{positionsGrouped.map(positions => {
const keys = Object.keys(positions);
return (
<div>
{positions[keys[0]].map(position => {
return <div>{this.renderPosition(position)}</div>;
})}
</div>
);
})}
</div>
);
}
}
export default Positions;
Inside your parent file
import React from "react";
import ReactDOM from "react-dom";
import Position from "./test";
import "./styles.css";
function App() {
var positionGroup = [
{
a: [
{
name: "hello",
title: "sdfd"
},
{
name: "hello",
title: "sdfd"
},
{
name: "hello",
title: "sdfd"
}
]
},
{
b: [
{
name: "hello",
title: "sdfd"
},
{
name: "hello",
title: "sdfd"
},
{
name: "hello",
title: "sdfd"
}
]
}
];
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Position positionsGrouped={positionGroup} />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
The return value of forEach is undefined no matter what you return in callback function. use map instead.
class Positions extends React.Component {
getExpiry(position) {
return `${position.ExpiryDay}-${position.ExpiryMonth}-${position.ExpiryYear}`;
}
render() {
return (
<div>
{this.props.positionsGrouped.map(positions => (
<div>
{positions.map((position) => (
<div>
<label>{this.getExpiry(position)}</label>
</div>
))}
</div>
))}
</div>
);
}
}
I changed your code a little to make it more concise.
Here is a demo.
I want to access this inside an array. Specifically,
this.props.categoryOpen.toString()
throws an error when used as follows.
https://codesandbox.io/s/23l3p906z
import React, { Component } from "react";
class Child extends Component {
render() {
return (
<div>
{this.props.categoryOpen.toString()}
{this.rows.map(row => (
<div>
{row.cells.map(cell => (
<div key={cell.label}>
{cell.label}: {cell.data}
</div>
))}
</div>
))}
</div>
);
}
rows = [
{
cells: [
{
label: "Cell A",
data: {this.props.categoryOpen.toString()}, // breaks
//data: "Foo" // works
},
{
label: "Cell B",
data: "Bar"
}
]
}
];
}
export default Child;
An arrow function also throws an error.
rows = () => [...
How can I access this?
Remove {} around data. It should work
Updated codesandbox: https://codesandbox.io/s/y0oo2v1xvv
Don't statically define your rows like that, or in the constructor, because they won't update when the input props change. In order to have the component re-render automatically when the props change you need to re-generate the rows in the render function.
So just make a method in your component called getRows (or something) and call that from render. That'll have the side effect of making this properly and normally accessible too.
class Child extends Component {
getRows() {
return [
{
cells: [
{
label: "Cell A",
data: this.props.categoryOpen.toString(),
},
{
label: "Cell B",
data: "Bar"
}
]
}
];
}
render() {
const rows = this.getRows()
return (
<div>
{this.props.categoryOpen.toString()}
{rows.map(row => (
<div>
{row.cells.map(cell => (
<div key={cell.label}>
{cell.label}: {cell.data}
</div>
))}
</div>
))}
</div>
);
}
}
export default Child;
Of course you could just generate rows inline in the render method too, but breaking it out into its own method can help with readability.
Just remove the curly braces
{
label: "Cell A",
data: this.props.categoryOpen.toString(), // remove curly braces
//data: "Foo" // works
}
As pointed by other comments you have a syntax error in the data field definition.
Also I did not know this but apparently you can make reference to the lexical context (which is the current component instance here) in class field definition. Worth noting they are not part of the language yet and I would advise to use the constructor for this, which is equivalent as per the Babel transform.
rows is a field which is defined at construction time, if you want to refer to the current instance you need to use the constructor
class Child extends Component {
constructor () {
this.rows = [
{
cells: [
{
label: "Cell A",
data: this.props.categoryOpen.toString(), // breaks
//data: "Foo" // works
},
{
label: "Cell B",
data: "Bar"
}
]
}
];
}
render() {
return (
<div>
{this.props.categoryOpen.toString()}
{this.rows.map(row => (
<div>
{row.cells.map(cell => (
<div key={cell.label}>
{cell.label}: {cell.data}
</div>
))}
</div>
))}
</div>
);
}
}
wrap variable with ticks like this.
data: `${this.props.categoryOpen.toString()}`,
this
should be fine
I'm using react-select to collect tags that has been defined in a mongodb collection called tags, I need to insert my tags in array options[] ins state the same way it's in staticOptions[].
issue: console.log(name) in my handleOptions() is logging only the first item in for loop only.
Question: How to return an array of objects from tags collection to this.state.options to be just like the staticOptions?
//-------create class: Addgifts-----
export default class Addgifts extends Component{
constructor(){
super()
this.state={
value: [],
options: [],
staticOptions: [{ label: 'Chocolate', value: 'chocolate' },
{ label: 'Vanilla', value: 'vanilla' },
{ label: 'Strawberry', value: 'strawberry' },
]
}
}
//-------Handle options--------
handleOptions(){
let KeyWords = this.props.tags;
for (i=0 ; i<KeyWords.length ; i++){
return KeyWords[i].map.name((names)=>{
console.log(name);
return(
this.state.options.push({label:{name},value:{name}});
)
}
});
}
}
//-----Select Change----
handleSelectChange (value) {
console.log('You\'ve selected:', value);
this.setState({
value
});
}
//-----Select package----
<div>
<Select
multi={true}
value={this.state.value}
placeholder="Select all KeyWord(s)"
options={this.handleOptions()}
onChange={this.handleSelectChange.bind(this)} />
</div>
//-----subscribing tags from mongodb----
Addgifts.propTypes={tags: PropTypes.array.isRequired,};
export default createContainer(()=>{
Meteor.subscribe('tags');
return {gifts: Gifts.find({},{sort:{name:-1}}).fetch(),};
},Addgifts);
First, you should transfer the data in props to state in componentDidMount() or (componentWillReceiveProps() if the props are getting updated with new props) and directly pass options to the select box. Doing it in handleOptions() is not a good idea.
Second, the i believe the logic inside handleOptions is not correct for iterating over an array of object.
Do something like this.
componentDidMount: function() {
var options = [];
for (i=0 ; i<this.props.tags.length ; i++){
var name = this.props.tags[i].name;
console.log(name);
options.push({label:name, value: name});
}
this.setState({options: options});
},
render: function() {
return (
<div>
<Select
multi={true}
value={this.state.value}
placeholder="Select all KeyWord(s)"
options={this.state.options}
onChange={this.handleSelectChange.bind(this)} />
</div>
);
}