I am using React and I am getting the following error: TypeError: Cannot set property 'innerHTML' of null. I did see some questions related to this error but I did not find any that helped me.
//React
class Pagination extends Component {
pagincationScript = (totalPages, page) =>{
const ulTag = document.querySelector("ul");
let liTag = '';
if(page > 1){
liTag = `<li className="Btn Previous"><span><i>←</i>Previous</span></li>`
}
ulTag.innerHTML = liTag;
}
render() {
return (
<div className="Pagination">
<ul>
{this.pagincationScript(10, 5)}
</ul>
</div>
)
}
}
export default Pagination;
When writing React you should always be writing JSX, like in the render function you wrote. To render the <li> separately you can do something like the following:
function Li() {
return (
<li className="Btn Previous"><span><i>←</i>Previous</span></li>
)
}
export default function Pagination() {
// Loop as many times as needed
const lis = [];
for (let i = 0; i < 10; i++) {
lis.push(<Li key={i} />);
}
return (
<div className="Pagination">
<ul>
{lis}
</ul>
</div>
)
}
I am trying to use the method .contact() to push on element in my old_array to my new_array.
I have one button on each element in the array like this:
´´´
<li key={i}>
{{character.name} + "is" + {character.age} + "years old"}
<button onClick={this.addToNewArray}>Fav</button>
</li>
´´´
so as you can see each element got a seperate id. Now i want to click the button to push that element to a new array . (i get data from API that i .map() into my old_array) My function looks like this:
´´´
constructor(props){
super(props);
this.state = {
old_arary: [],
new_array: []
}
}
addToNewArray = () => {
let new_array = this.state.new_array.contact(this.state.old_array);
this.setState({ new_array: new_array})
}
´´´
This is where i want my output:
´´´
<li>
{this.state.new_array}
</li>
´´´
First :
in your question , you are using contact() everywhere, and I think there is no such function for array in JS :) , that should be concat()
Second :
You can use ES6 for lower code, something like this:
let new_array = [...this.state.new_array , ...this.state.old_array];
this.setState({ new_array });
Third :
Change this
<li>
{this.state.new_array}
</li>
To :
{
this.state.new_array.map((obj,index) => (
<li key={index}>
{obj.name}
</li>
))
}
I'm currently trying to coding a react app that would do the following:
- Create a list of questions from an array using a map function.
- Making each list element clickable using a onClick prop
- The linked onClick method changes the state in another file with my 'qsChange' prop.
I had a hard time making my list clickable and finally managed following this question: React: trying to add an onClick to a li tag but the click handler function is undefined
However, now I cannot make it so that my variable 'choice' returns a defined value. I would want var choice to be equal to "A ?", "B ?" or "C ?" depending on which I click.
Here's my code:
var questions = ["A ?", "B ?", "C ?"];
var Questions = React.createClass({
handleClick: function() {
var visibility;
if(this.props.visibility) {
document.getElementById('second').style.display = 'none';
visibility = false;
this.props.onChange(visibility);
} else {
document.getElementById('second').style.display = 'block';
visibility = true;
this.props.onChange(visibility);
}
},
/* Here is where my problem lies */
onItemClick: function(e){
var choice = e.target.key;
this.props.qsChange(choice);
alert(choice);
},
render: function() {
return (
<div className="bigqs">
<div id="first" className="small" style={firstStyle}>
<h1>Question :</h1>
<button style={btnStyle} onClick={this.handleClick}>
<img id="arrow" src="../../img/arrow.png" />
</button>
<h3 id="selectedQuestion">{this.props.selected}</h3>
</div>
<div id="second" className="small" style={{display: 'none'}}>
<h4>
<ul>
{questions.map(function(qs, i) {return <li key={qs[i]} onClick={this.onItemClick}>{qs}</li>;}, this)}
</ul>
</h4>
</div>
</div>
);
}
});
I am still a newbie, so please be indulgent ;-)
I hope I was clear enough.
Ps: I have also tried this guide but it didn't work for me: http://derpturkey.com/react-pass-value-with-onclick/
Instead of grabbing the question from target, you can pass question through to your handler. Also, since inside map qs is a string, qs[i] will be getting the character in the string from that index. You just need to make sure your key is unique.
onItemClick: function(choice) {
this.props.qsChange(choice)
alert(choice)
},
render() {
return (
<div>
...
{questions.map(qs =>
<li key={qs} onClick={() => this.onItemClick(qs)}>{qs}</li>
)}
...
</div>
)
}
In fact, your intermediate function isn't doing much, you can just call your props function inside render:
render() {
return (
<div>
...
{questions.map(qs =>
<li key={qs} onClick={() => this.props.qsChange(qs)}>{qs}</li>
)}
...
</div>
)
}
I've got the following component in ReactJS
var MainMenu = React.createClass({
render: function() {
console.log(this.props.groupsData);
var categories = this.props.groupsData.objects.map(function(obj){
return (<li>{obj.display_name}</li>);
});
return (<div className="MainMenu">
<ul className="nav nav-pills">{categories}</ul>
</div>);
}
});
Now, I wish to add className='active' to the <li> element if its the first in the map. How would I achieve this?
Use JS in your expression
var MainMenu = React.createClass({
render: function() {
console.log(this.props.groupsData);
var categories = this.props.groupsData.objects.map(function(obj, index) {
// You can have a JavaScript expression in your expression
return ( <li className={index == 0 ? 'active' : ''}> <a href="#">{
obj.display_name
}</a></li> );
});
return ( <div className="MainMenu" >
<ul className="nav nav-pills" >{
categories
}</ul>
</div> );
}
});
How do i get this :
<li>
<div class='myClass1'>myData1</div>
<div class='myClass2'>myData2</div>
<div class='myClass3'>myData3</div>
<div class='myClass4'>myData4</div>
</li>
from this code
var data1 = {"Columns":[{"Title":"Title1","HTMLClass":"g1_Title"},{"Title":"Title2","HTMLClass":"g2_Title"},{"Title":"Title3","HTMLClass":"g3_Title"}],"Rows":[{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]},{"Cells":["Cell0","Cell1","Cell2"]}]};
var GridRow = React.createClass({
render: function() {
var data = [], columns;
// puts all the data in to a better structure (ideally the props would have this structure or this manipulation would be done on onReceiveProps)
if(this.props.columns){
for(var ii = 0; ii < this.props.columns.length; ii++){
data.push({
class: this.props.columns[i].HTMLClass,
contents: this.props.Cell[i]
})
}
}
// Its best to map JSX elements and not store them in arrays
columns = data.map(function(col) {
return <div className= + {col.class}> {col.contents} </div>;
});
return (
<div>
<li>
{columns}
</li>
</div>
);
}
});
var GridHead = React.createClass({
render: function() {
if(this.props.data){
var cell = this.props.data.Title;
var htmlClass = this.props.data.HTMLClass;
}
return (
<div className={htmlClass}>{cell}</div>
);
}
});
var GridList = React.createClass({
render: function() {
if(this.props.data){
var header = this.props.data.Columns.map(function (columns) {
return (
<GridHead data={columns} />
);
});
var row = this.props.data.Rows.map(function (row, i) {
return (
<GridRow columns={data1.Columns} cells={row.Cells} key={i} />
);
});
}
return (
<ul>
<li>{header}</li>
{row}
</ul>
);
}
});
var GridBox = React.createClass({
render: function(){
return (
<GridList data={data1} />
);
}
});
The output right now is this
In file "~/Scripts/Grid.jsx": Parse Error: Line 26: XJS value should
be either an expression or a quoted XJS text (at line 26 column 35)
Line: 52 Column:3
As your question initially asked was to do with just the GridRow component and nothing else I have not touched any other component.
Your main problem was you were assigning className = + //something in your GridRow component which isn't the correct way to assign. There were other errors like missing div tags.
Better GridRow
When the component mounts a columndata variable is created and is populated with formatted data using formatData();.
I do not recommend you do data formatting in this component (although it is doable). You should either format your data at a top level component and pass down formatted data or accept data in the correct structure.
My GridRow component to this:
var GridRow = React.createClass({
componentWillMount: function() {
this.columndata = [];
this.formatData();
},
formatData: function() { // Formats prop data into something manageable
if (this.props.columns && this.props.cells) {
for(var ii = 0; ii < this.props.columns.length; ii++){
this.columndata.push({
class: this.props.columns[ii].HTMLClass,
contents: this.props.cells[ii]
})
}
this.forceUpdate(); // Forces a rerender
}
},
componentDidUpdate: function(prevProps, prevState) {
// If this component receives the props late
if (!prevProps.cells && !prevProps.columns) {
this.formatData();
}
},
render: function() {
var columns;
// Its best to map JSX elements and not store them in arrays
columns = this.columndata.map(function(col) {
return <div className={col.class}> {col.contents} </div>;
});
return (
<div>
<li>
{columns}
</li>
</div>
);
}
});
I think it's important to note that you should avoid storing JSX elements in arrays.
I think you were basically on the money, except you were missing classname and div tags.