I'm new to React and stuck trying assign parent's function to children created dynamically
class Row extends React.Component {
handleStateChange() {
console.log(this); //just for test
}
render() {
let notes = [],
categoryId = this.props.rowNo;
bonuses.forEach(function (bonus, i) {
let id = 'cell_' + categoryId.toString() + (i + 1).toString();
notes.push(<NoteCell bonus={bonus}
songName={id + '.mp3'}
id={id}
key={id}
// that is the point
handleRowStateChange={this.handleStateChange}
/>);
});
return (
<div className="row clearfix">
{notes}
</div>
)
}
I get Cannot read property 'handleStateChange' of undefined error.
What am i doing wrong?
scope of this inside callback function refer to calling object, not to the react class.So Use ()=> instead of function.
handleStateChange() {
console.log(this); //just for test
this.setState({parentState:value})
}
bonuses.forEach((bonus, i) =>{
let id = 'cell_' + categoryId.toString() + (i + 1).toString();
notes.push(<NoteCell bonus={bonus}
songName={id + '.mp3'}
id={id}
key={id}
// that is the point
handleRowStateChange={this.handleStateChange.bind(this)}
/>);
});
Your this is refering to the bonuses.forEach(function function rather than this from your component class. An arrow function should eliminate that problem.
bonuses.forEach((bonus, i) => {
As an aside, outside of React if you are not using ES6 then you could do this by getting copy of this at the top of your function and then use it inside your function:
render() {
let notes = [],
categoryId = this.props.rowNo
self = this;
...
handleRowStateChange={self.handleStateChange}
But you still have another problem. When you get inside the handleStateChange function, it too will have its own this. You can solve that with a constructor:
class Row extends React.Component {
constructor (props) {
super(props);
this.handleStateChange = this.handleStateChange.bind(this);
}
...
Related
Try to use functional programming to create an object with external functions to reduce memory usage.
The function is
//increment no of test cases
function incrNoOfTestCases(inputObj){
let hits = inputObj.hits;
console.log(`function says: ${hits}`)
return {...inputObj, hits: (hits || 0) + 1};
}
The creator function is
const test = function(testDecription){
let state = {hits:0};
state.getState = ()=> testDecription;
state.incHits = () => state = incrNoOfTestCases(state);
state.getHits = () => state.hits || 0;
return state;
}
When I do the following test, I can change the hits by assigning a property with to the function.
test1.incHits().hits=10; //mutable!!
console.log(test1.getHits()); //gives 10
console.log(test1.incHits().hits); //gives function says: 10 and then 11
test1.hits=20; //immutable
console.log(test1.getHits()); //gives 10
I tried various alternatives, finally came up with declaring the function to increment the testcases in the creator function. I am looking for an explanation why the property is mutable not for a working case.
In the first version the function was
function incrNoOfTestCases(inputObj){
return {...inputObj, hits: (inputObj.hits || 0) + 1};
}
In this case I also expected the inputObj.hits not to be mutable by incrNoOfTestCases.hits, but wasn't either.
It seems JavaScript firstly assigns incrNoOfTestCases.hits to state before executing the function. Is this correct? Can you explain why?
There is nothing functional about this code. In functional programming you don't want small logical units to handle their state independently. That's OOP. Using a closure is just the same as using a class if you mutate the value.
This is more functional although it probably doesn't work the way you would like.
const Test = (description, hits = 0) => ({
getState: () => description,
incHits: () => Test(description, hits + 1),
getHits: () => hits
})
const test1 = Test('description')
const test2 = test1.incHits(); // incHits returns a new instance of Test
console.log(test2.getHits())
And this would have done the same thing
class Test {
constructor(description, hits = 0) {
this.description = description;
this.hits = hits;
}
static of (description) { return new Test(description) }
getState () { return this.description}
incHits () { return new Test(this.description, this.hits + 1); }
getHits () { return this.hits }
}
const test1 = Test.of('description');
const test2 = test1.incHits();
Yet another way to do it
const Test = (description, hits = 0) => ({ description, hits, type: 'Test' });
export const getState = ({ description }) => description;
export const incHits = ({ description, hits }) => Test(description, hits + 1);
export const getHits = ({ hits }) => hits;
export const of = (description) => Test(description);
import * from './Test'
const test1 = Test.of('description');
const test2 = Test.incHits(test1);
I'm a newbie in React. I have 6 divs and whenever I call foo() I want to add a number to the first div that's empty.
For example, let's say that the values of the six divs are 1,2,0,0,0,0 and when I call foo(), I want to have 1,2,3,0,0,0.
Here is what I've tried:
var index = 1;
function foo() {
let var x = document.getElementsByClassName("square") // square is the class of my div
x[index-1].innerHTML = index.toString()
index++;
}
I don't know when I should call foo(), and I don't know how should I write foo().
The "React way" is to think about this is:
What should the UI look like for the given data?
How to update the data?
Converting your problem description to this kind of thinking, we would start with an array with six values. For each of these values we are going to render a div:
const data = [0,0,0,0,0,0];
function MyComponent() {
return (
<div>
{data.map((value, i) => <div key={i}>{value}</div>)}
</div>
);
}
ReactDOM.render(<MyComponent />, document.body);
<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>
Now that we can render the data, how are we going to change it? From your description it sounds like every time a function is called, you want change the first 0 value in the array to another value. This can easily be done with:
// Find the index of the first 0 value
const index = data.indexOf(0);
if (index > -1) {
// if it exists, update the value
data[index] = index + 1;
}
To make this work properly with React we have to do two things: Keep track of the updated data in state, so that React rerenders the component when it changes, and update the data in a way that creates a new array instead of mutating the existing array.
You are not explaining how/when the function is called, so I'm going to add a button that would trigger such a function. If the function is triggered differently then the component needs to be adjusted accordingly of course.
function update(data) {
const index = data.indexOf(0);
if (index > -1) {
data = Array.from(data); // create a copy of the array
data[index] = index + 1;
return data;
}
return data;
}
function MyComponent() {
var [data, setData] = React.useState([0,0,0,0,0,0]);
return (
<div>
{data.map((value, i) => <div key={i}>{value}</div>)}
<button onClick={() => setData(update(data))}>Update</button>
</div>
);
}
ReactDOM.render(<MyComponent />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
You would use state to hold the value and then display the value of that variable.
If you're using functional components:
const App = () => {
const [values, setValues] = React.useState([0, 0, 0, 0, 0, 0]);
const [index, setIndex] = React.useState(0);
const foo = () => {
const tempValues = [...values];
tempValues[index] = index;
setValues(tempValues);
setIndex((index + 1) % values.length);
}
return (
<div>
{ values.map((value) => <div key={`square-${value}`}>{value}</div>) }
<button onClick={ foo }>Click me</button>
</div>
);
};
In class-based components:
constructor(props) {
super(props);
this.state = {
values: [0, 0, 0, 0, 0, 0],
index: 0
};
this.foo = this.foo.bind(this);
}
foo() {
const tempValues = [...values];
const newIndex = index + 1;
tempValues[newIndex] = newIndex;
this.setState({
values: tempValues,
index: newIndex
});
}
render() {
return (
<div>
{ values.map((value) => <div key={`square-${value}`>value</div>) }
<button onClick={ this.foo}>Click me</button>
</div>
);
}
If you need to set the innerHTML of a React component, you can try this:
return <div dangerouslySetInnerHTML={foo()} />;
the foo() here returns the value you want to post in the div.
But in my opinion, your way of thinking on this problem is wrong.
React is cool, but the logic is a bit different of common programming :D
The ideal approach would be to have the divs created by React (using its render method). Then you can pass a variable from array, which is stored in your state. You then just need to change this array within the state and it'll reflect in your view. If you need a working example, just let me know.
However, if you want to update the divs that are not created using react, then you need to use a dirty approach. I would suggest not to use react if you can't generate the view from react.
React is good to separate the concerns between the view and the data.
So the concept of state for this example is useful to store the data.
And the JSX, the React "template" language, to display the view.
I propose this solution:
import React from "react";
class Boxes extends React.Component {
state = {
divs: [1, 2, 3, 0, 0, 0]
};
add() {
// get the index of the first element equals to the condition
const index = this.state.divs.findIndex(elt => elt === 0);
// clone the array (best practice)
const newArray = [...this.state.divs];
// splice, i.e. remove the element at index AND add the new character
newArray.splice(index, 1, "X");
// update the state
// this is responsible, under the hood, to call the render method
this.setState({ divs: newArray });
}
render() {
return (
<div>
<h1>Boxes</h1>
{/* iterate over the state.divs array */}
{this.state.divs.map(function(elt, index) {
return (
<div
key={index}
style={{ border: "1px solid gray", marginBottom: 10 }}
>
{elt}
</div>
);
})}
<button onClick={() => this.add()}>Add a value</button>
</div>
);
}
}
export default Boxes;
I am writing simple application which need to rerender specified content.
My ideas about resolving the issue was to provide initializing data in constructor because something breaks react structure?But maybe helpful might be a tip how to map two dimensional array in render method. Propably here is the problem?
function Pool(props) {
return(
<p className={`${props.row}`}></p>
);
}
export default class Board extends React.Component {
constructor(props){
super(props);
this.state = {
mainArray: [],
};
}
createBoard() {
let children=[];
let array=[];
for(let i=0;i<20;i++){
children=[];
for(let j=0;j<20;j++){
children.push(<Pool key={`${i}${j}`} row={`${square1}`}/>);
}
array.push(<div key={`${i}`}>{children}</div>);
}
this.state.mainArray=array;
return this.state.mainArray;
}
startGame = () => {
let array1=[];
array1=this.state.mainArray.slice;
let i=6;
for(let j in array1[6]){
array1[6][j]=<Pool key={`${i}${j}`} row={`${square2}`}/>;
}
this.setState({mainArray: array1});
}
render() {
return (
<div className="main">
{this.createBoard()}
<button onClick={this.startGame}>Start the game!</button>
</div>
);
}
}
I am trying to change the colour of the sixth row for example.Regards
This is incorrect:
let array1=[];
array1=this.state.mainArray.slice;
mainArray.slice() is a copy, but mainArray.slice is a function.
Instead, begin with
let array1 = this.state.mainArray.slice();
I'm new in ReactJS, but already got some problem I cannot resolve...
I have React component called Tree. This component must receive array of some data. Every element in this array must be rendered as a special child component called Department.
At the beginning, Tree has state {departments: []}, so, tree must be empty and it is. But then I change Tree's state, I set new array at departments, I see no child elements.
And the point is, that tree's state really updates, and function "render" is really called, and when I run thru departments, I get the right number of iterations in my cycle (checked with console.log). But still no child element appears.
My code works, I tried to render tree component with fixed departments and set this state in the constructor as initial state. Tree and child components worked fine.
So, I cannot imagine, what might be wrong.
Here is my code, my class Tree
class Tree extends React.Component {
constructor(props) {
super(props)
this.state = {
departments: []
}
this.componentDidMount = this.componentDidMount.bind(this);
}
componentDidMount() {
var xhr = new XMLHttpRequest();
xhr.open('GET', startAdress + '/tutors' + '/getAll', true);
xhr.send();
var tree = this;
xhr.onreadystatechange = function() {
if (xhr.readyState != 4) return;
if (xhr.status != 200) {
alert(xhr.status + ': ' + xhr.statusText);
} else {
var newDepts = JSON.parse(xhr.responseText);
if (Array.isArray(newDepts.content)) {
tree.setState({
departments: newDepts.content
});
}
}
};
}
render() {
var a = this;
console.log(JSON.stringify(this.state.departments));
console.log(this.state.departments.length);
return ( <div className={"Tree"}>
{
this.state.departments.forEach(function (department) {
console.log("creating branches");
return (
<Department key={department.name} department={department} />
);}
) }
</div> )
}
}
And here is my child component Department. It uses another component, called TreeLine, but I think it is not necessary to put it here.
class Department extends React.Component {
constructor(props) {
super(props)
this.state = {
}
}
render() {
console.log("department " + JSON.stringify(this.props.department));
return (
<div className={'Department'}>
<TreeLine key={this.props.department.name } classNamePostfix={"Dep"}
item={this.props.department} />
{this.props.department.items.forEach(function (item) {
return (
<TreeLine key={item.name} classNamePostfix={"Item"} item={item} />
)})
}
</div>
);
}
}
Thanks in advance!
Instead of .forEach() you should be using .map().
forEach goes through every element of the array and does something with it, but nothing is returned by forEach, even if the callback returns something. On the other hand, map creates a new array with what is being returned in the callback function as an element in that new array.
const a = [1,2,3].forEach((i) => {return <span>{i}</span>})
// a = undefined
const b = [1,2,3].map((i) => {return <span>{i}</span>})
// b = [<span>1</span>, <span>2</span>, <span>3</span>]
I used this article as an example (React way), but it is not working for me. Please point me to my mistake, as I can't understand what's wrong.
This is the error I see:
Uncaught TypeError: this.props.onClick is not a function
Here is my code:
// PARENT
var SendDocModal = React.createClass({
getInitialState: function() {
return {tagList: []};
},
render: function() {
return (
<div>
{
this.state.tagList.map(function(item) {
return (
<TagItem nameProp={item.Name} idProp={item.Id} onClick={this.HandleRemove}/>
)
})
}
</div>
)
},
HandleRemove: function(c) {
console.log('On REMOVE = ', c);
}
});
// CHILD
var TagItem = React.createClass({
render: function() {
return (
<span className="react-tagsinput-tag">
<span>{this.props.nameProp}</span>
<a className='react-tagsinput-remove' onClick={this.HandleRemove}></a>
</span>
)
},
HandleRemove: function() {
this.props.onClick(this);
}
});
Thanks in advance!
The issue is that this inside the map callback does not refer to the React component, hence this.HandleRemove is undefined.
You can set the this value explicitly by passing a second argument to map:
this.state.tagList.map(function() {...}, this);
Now this inside the callback refers to the same value as this outside the callback, namely the SendDocModal instance.
This has nothing to do with React, it's just how JavaScript works. See How to access the correct `this` context inside a callback? for more info and other solutions.
Try the following:
var SendDocModal = React.createClass({
getInitialState: function() {
var item = {};
item.Name = 'First';
item.Id = 123;
var item2 = {};
item2.Name = 'Second';
item2.Id = 123456;
return {tagList: [item,item2]};
},
HandleRemove: function(c){
console.log('On REMOVE = ', c);
},
render: function() {
return (<div>
{this.state.tagList.map(function(item){
return(
<TagItem nameProp={item.Name} idProp={item.Id} key={item.Id} click={this.HandleRemove}/>
)}, this)}
</div>
)
}
});
// CHILD
var TagItem = React.createClass({
handleClick: function(nameProp)
{
this.props.click(nameProp);
},
render: function(){
return(
<span className="react-tagsinput-tag" ><span onClick={this.handleClick.bind(this, this.props.nameProp)}>{this.props.nameProp}</span><a className='react-tagsinput-remove' ></a></span>
)
}
});
Few changes:
Added 'this' after the tagList mapping. To be honest I am not entirely sure why - perhaps a more experienced programmer can tell us.
Added a key to each TagItem. This is recommended and an the console will inform you that you should do this so that if the state changes, React can track each item accordingly.
The click is passed through the props. See React js - having problems creating a todo list