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();
Related
I have a block of code like this:
randomArray(arrayOfImage){
//algorithm goes here
return shuffledArray;
}
shuffle(){
this.randomArray(images);
this.forceUpdate();
}
render(){
let array = this.randomArray(images); //images is a declared array
let squares = array.map((item,index) => <Squares/> )
return(
<div>
<div id='square-container'>
{squares}
</div>
<button className='btn' onClick = {this.shuffle.bind(this)}>Shuffle</button>
</div>
)
}
Basically, I have an array images declared. The function randomArray() return a shuffled version of images. And then, for each of the item inside the shuffled array array the browser will render a div with the given props.
Now I have a button so that the user can shuffle the array themselves. Here, I use forceupdate() because even though the array is shuffled when the button is clicked, the DOM won't update because there is no changes in the state.
It works! But since using forceupdate() is not encouraged, how should I make this less... say, amateur?
Sure, React will render an element again as soon as it's state gets changed, or it receives new props from it's parent component (or a state library).
You can read more about how react handles the rendering in their documentation
So, to handle this, just create a new array based on your original one, and then set it to the state again.
In the olden days, you would require a class for setting the state on component level
const target = document.getElementById('container');
class ArrayOfImages extends React.Component {
constructor() {
// don't forget to call super
super();
// set the initial state
this.state = {
images: [
'https://c402277.ssl.cf1.rackcdn.com/photos/18357/images/featured_story/Medium_WW252652.jpg?1576698319',
'https://c402277.ssl.cf1.rackcdn.com/photos/882/images/circle/African_Elephant_7.27.2012_hero_and_circle_HI_53941.jpg?1345532748',
'https://c402277.ssl.cf1.rackcdn.com/photos/1732/images/circle/Asian_Elephant_8.13.2012_Hero_And_Circle_HI_247511.jpg?1345551842'
]
};
// bind the function we are going to call from the render function
this.shuffle = this.shuffle.bind( this );
}
shuffle() {
// create a shallow copy of the state object
const copyOfState = this.state.images.slice();
// shuffle it a bit (Durstenfeld shuffle: http://en.wikipedia.org/wiki/Fisher-Yates_shuffle#The_modern_algorithm)
for (let i = copyOfState.length; i--;) {
let target = Math.floor( Math.random() * (i+1) );
[copyOfState[i], copyOfState[target]] = [copyOfState[target], copyOfState[i]];
}
// update the existing state with the new array
this.setState({ images: copyOfState });
}
render() {
const { images } = this.state;
return (
<div>
<h1>Some elephants</h1>
{ images.map( img => <img key={ img } alt={ img } src={ img } /> ) }
<div><button type="button" onClick={ this.shuffle }>Shuffle images</button></div>
</div>
);
}
}
ReactDOM.render( <ArrayOfImages />, target );
<script id="react" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.2/react.js"></script>
<script id="react-dom" src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/15.6.2/react-dom.js"></script>
<div id="container"></div>
But now, you could do it by using the useState helper for that
// this would be an import in normal code
const { useState } = React;
const target = document.getElementById('container');
const shuffle = arr => {
// create a shallow copy of the array
const copy = arr.slice();
// shuffle it a bit (Durstenfeld shuffle: http://en.wikipedia.org/wiki/Fisher-Yates_shuffle#The_modern_algorithm)
for (let i = copy.length; i--;) {
let target = Math.floor( Math.random() * (i+1) );
[copy[i], copy[target]] = [copy[target], copy[i]];
}
return copy;
}
const ArrayOfImages = () => {
const [images, updateImages] = useState([
'https://c402277.ssl.cf1.rackcdn.com/photos/18357/images/featured_story/Medium_WW252652.jpg?1576698319',
'https://c402277.ssl.cf1.rackcdn.com/photos/882/images/circle/African_Elephant_7.27.2012_hero_and_circle_HI_53941.jpg?1345532748',
'https://c402277.ssl.cf1.rackcdn.com/photos/1732/images/circle/Asian_Elephant_8.13.2012_Hero_And_Circle_HI_247511.jpg?1345551842'
]);
return (
<div>
<h1>Some elephants</h1>
{ images.map( img => <img key={ img } alt={ img } src={ img } /> ) }
<div><button type="button" onClick={ () => updateImages( shuffle( images ) ) }>Shuffle images</button></div>
</div>
);
}
ReactDOM.render( <ArrayOfImages />, target );
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id="container"></div>
The shuffle algorithm comes from this answer
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 trying to display Linechart using chartist.js. I have used reduce() to extract data from props. I have keys as year and values as average value in averages object. But I am not able to display the line chart as I am not able to see any lines on chart I am getting type error. I have created React component and inside componentDidMount() I have added my line chart code. I think the issue is related to javascript/reactjs and not chartist.js
Code:
class Linechart extends Component {
constructor(props){
super(props);
}
componentDidMount(){
const totals = this.props.bowldata.reduce((a, {season, econ}) => {
if (!a[season]) {
a[season] = {sum: 0, count: 0};
}
if(econ !== null){
a[season].count++;
a[season].sum += parseInt(econ);
}
return a;
}, {});
const averages = {};
Object.keys(totals).forEach(key => {
averages[key] = totals[key].sum / totals[key].count;
});
console.log(averages);
const series = Object.values(averages);
const labels = Object.keys(averages);
const data = {
series:series,
labels:labels
}
this.linechart = new Chartist.Line('.ct-line-chart', data, options);
}
render(){
return(
<div>
<div className="col-md-3 col-xs-6 ct-line-chart">
{this.linechart}
</div>
</div>
)}
}
export default Linechart ;
Using .forEach() I am trying to create new array of objects with sum/count as a value and key as year.
Screenshot:
Change your code like this see if the problem is gone. In docs it shows series should be a multidimensional array.
const data = {
series:[series],
labels:labels
}
So, first of all, what I'm trying to do is the following: I have a few divs that contain some text and an image. All the data for the divs is stored in a state array. You can also add divs and delete whichever div you desire. What I would like to implement now, is to change the picture when the user clicks on an image. There is a preset image library and whenever the user clicks on the image, the next image should be displayed.
Here is some relevant code:
let clicks = 0;
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
data : [
createData( someimage, "Image 1"),
createData( anotherimage, "Image 2"),
createData( thirdimage, "Image 3"),
createData( fourthimage, "Image 4"),
],
imgs : [imgsrc1,imgsrc2, imgsrc3, imgsrc4],
}
}
newIcon (n) {
let newStateArray = this.state.data.slice();
let newSubStateArray = newStateArray[n].slice();
if(clicks === 1) {
newSubStateArray[0] = this.state.imgs[0];
this.setState({imgsrc:newSubStateArray});
clicks++;
} else if (clicks === 2) {
newSubStateArray[0] = this.state.imgs[1];
this.setState({imgsrc:newSubStateArray});
clicks++;
} else if (clicks === 3) {
newSubStateArray[0] = this.state.imgs[2];
this.setState({imgsrc:newSubStateArray});
clicks++;
} else if (clicks === 4) {
newSubStateArray[0] = this.state.imgs[4];
this.setState({imgscr:newSubStateArray});
clicks++;
}
}
render () {
let { data }= this.state;
return(
<div>
{data.map((n) => {
return(
<Child imgsrc={n.imgsrc} key={n} newIcon={this.newIcon.bind(this, n)} header={n.header} />
);
})}
</div>
);
}
A few sidenotes: createArray is a function to create the sub-arrays and can probably be ignored for this question. What is important to know is, that the first element is called imgsrc, and the second element is called
So, something is going wrong here but I'm not sure what it is exactly. My guess is, that I'm not properly accessing the values within the arrays. Above, you can see that I tried to slice the arrays and to then allocate the new value. Another problem I've encountered, is that n comes up as undefined, when I try to call it from my newIcon()-function.
I'm kind of lost here, as I'm quite new to React so any sort of hints and suggestions are welcome.
I would do away with all that code in newIcon, and keep clicks as part of the state. If you have an array of images then you can use clicks as a pointer to the next image that should be shown.
In this example I've taken the liberty of adding in dummy images to help explain, and changed clicks to pointer as it makes more sense.
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
// clicks is called `pointer` here and initially
// is set to the first index of the imgs array
pointer: 0,
imgs: [
'https://dummyimage.com/100x100/000000/fff.png',
'https://dummyimage.com/100x100/41578a/fff.png',
'https://dummyimage.com/100x100/8a4242/fff.png',
'https://dummyimage.com/100x100/428a49/fff.png'
]
};
// Bind your class method in the constructor
this.handleClick = this.handleClick.bind(this);
}
// Here we get the length of the imgs array, and the current
// pointer position. If the pointer is at the end of the array
// set it back to zero, otherwise increase it by one.
handleClick() {
const { length } = this.state.imgs;
const { pointer } = this.state;
const newPointer = pointer === length - 1 ? 0 : pointer + 1;
this.setState({ pointer: newPointer });
}
render() {
const { pointer, imgs } = this.state;
// Have one image element to render. Every time the state is
// changed the src of the image will change too.
return (
<div>
<img src={imgs[pointer]} onClick={this.handleClick} />
</div>
);
}
}
DEMO
EDIT: Because you have more than one div with images the sources of which need to change, perhaps keep an array of images in a parent component and just pass a subset of those images down to each Image component as props which you then store in each component's state. That way you don't really need to change the Image component that much.
class Image extends React.Component {
constructor(props) {
super(props);
this.state = { pointer: 0, imgs: props.imgs };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
const { length } = this.state.imgs;
const { pointer } = this.state;
const newPointer = pointer === length - 1 ? 0 : pointer + 1;
this.setState({ pointer: newPointer });
}
render() {
const { pointer, imgs } = this.state;
return (
<div>
<img src={imgs[pointer]} onClick={this.handleClick} />
</div>
);
}
}
class ImageSet extends React.Component {
constructor() {
super();
this.state = {
imgs: [
'https://dummyimage.com/100x100/000000/fff.png',
'https://dummyimage.com/100x100/41578a/fff.png',
'https://dummyimage.com/100x100/8a4242/fff.png',
'https://dummyimage.com/100x100/428a49/fff.png',
'https://dummyimage.com/100x100/bd86bd/fff.png',
'https://dummyimage.com/100x100/68b37c/fff.png',
'https://dummyimage.com/100x100/c9a7c8/000000.png',
'https://dummyimage.com/100x100/c7bfa7/000000.png'
]
}
}
render() {
const { imgs } = this.state;
return (
<div>
<Image imgs={imgs.slice(0, 4)} />
<Image imgs={imgs.slice(4, 8)} />
</div>
)
}
}
DEMO
Hope that helps.
Try to bind the newIcon() method in the constructor, like this :
this.newIcon = this.newIcon.bind(this);
and in the render method call it normally without any bind :
this.newIcon(n)
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);
}
...