AESTHETICS
AESTHETICS

Reputation: 1049

Looping through certain elements in React

I am trying to loop through certain elements in React as i need to render part of an array per each view.

Array rendering has to start from ArrayIndexStart and end at ArrayIndexEnd.

Here is what i got:

var ObjectArray =[{"letter":"a"},{"letter":"b"},{"letter":"c"},{"letter":"d"},{"letter":"e"},{"letter":"f"},{"letter":"g"},{"letter":"h"},{"letter":"i"},{"letter":"j"},{"letter":"k"},{"letter":"l"},{"letter":"ł"},{"letter":"m"},{"letter":"n"},{"letter":"ń"},{"letter":"o"},{"letter":"ó"},{"letter":"p"},{"letter":"q"},{"letter":"r"},{"letter":"s"},{"letter":"ś"},{"letter":"t"},{"letter":"u"},{"letter":"v"},{"letter":"w"},{"letter":"x"},{"letter":"y"},{"letter":"z"},{"letter":"ź"},{"letter":"ż"}];

class ImageViewer extends React.Component {
    constructor() {
        super();
        this.state = {
            ArrayIndexStart: 0,
            ArrayIndexEnd: 8,
        }
        this.handleButtonLeft = this.handleButtonLeft.bind(this);
        this.handleButtonRight = this.handleButtonRight.bind(this);
    }
    handleButtonLeft(ArrayIndexStart, ArrayIndexEnd, event) {
        if (ArrayIndexStart >= 8) {
            this.setState({ ArrayIndexEnd: ArrayIndexEnd,ArrayIndexStart: ArrayIndexStart})
        }
        else {
            this.setState({ ArrayIndexEnd: 8,ArrayIndexStart: 0 })
        }
    }
    handleButtonRight(ArrayIndexStart, ArrayIndexEnd, event) { 
        if (ArrayIndexEnd <= ObjectArray.length) {
            this.setState({ ArrayIndexStart: ArrayIndexStart,ArrayIndexEnd: ArrayIndexEnd })
        }
        else  {
            this.setState({ ArrayIndexStart: 24,ArrayIndexEnd: ObjectArray.length })
        }
    }
    render() {
     return(
         
         <div>
         <button onClick={() => this.handleButtonLeft(this.state.ArrayIndexStart - 8, this.state.ArrayIndexEnd - 8)}>left</button>     
         <button onClick={() => this.handleButtonRight(this.state.ArrayIndexStart + 8, this.state.ArrayIndexEnd + 8)}>right</button>
         <div>
                   <p>{this.state.ArrayIndexStart}</p>
                    <p>{this.state.ArrayIndexEnd}</p>
        </div>
        </div>
     )
        for(var i = this.state.ArrayIndexStart;i<this.state.ArrayIndexEnd;i++)
        {
            return(
                <div>
                <p>{ObjectArray[i].letter}</p>
                </div>
            )
        }


     
    }}
    ReactDOM.render(
    <ImageViewer  />,
    document.getElementById('root')
    )
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
 <div id="root"></div>
I made this this code but double return type doesn't work and it is not possible to return more than 1 element from this for loop. I am not sure how to make it to work.

Upvotes: 1

Views: 512

Answers (2)

Alex Young
Alex Young

Reputation: 4039

All the code after your first return statement from render() is ignored - once the function had returned then its execution stops. It is often useful to use a tool like ESLint which can tell you when this is the case and guide you to writing better code.

In your case then you could loop over the ObjectArray with a for loop to create the elements you need, but it is much easier to use some functional programming techniques to do this within your JSX.

I have updated your snippet to show one way of doing this. It uses the slice function first:

ObjectArray.slice(startIndex, endIndex)

which returns a new array containing only the elements between start and end index. For example,

[1,2,3,4,5,6].slice(1, 3); // returns [2,3,4]

Once we have sliced the part of the array that we need, we can then use the map function to transform each element of the array into the form that we need.

ObjectArray.map(obj => obj.letter);

The map function takes a single argument (the lambda function obj => obj.letter) which will be applied in turn to each member of the array.

A simpler example might be doubling each number in an array:

[1,2,3,4].map(x => x * 2); // returns [2,4,6,8];

I have also made some changes to simplify your click handler functions. It uses the functional setState argument, i.e.

this.setState(previousState => {
    return {
        // state update object
    }
});

This is the correct approach to use when your next state depends on the value of your previous state - in your code the next index depends on the value of the previous index. It also greatly simplifies the JSX by allowing you to do:

onClick={this.handleButtonClick(increment)}

For both buttons, passing a negative increment to go "left" and a positive increment to go "right".

var ObjectArray =[{"letter":"a"},{"letter":"b"},{"letter":"c"},{"letter":"d"},{"letter":"e"},{"letter":"f"},{"letter":"g"},{"letter":"h"},{"letter":"i"},{"letter":"j"},{"letter":"k"},{"letter":"l"},{"letter":"ł"},{"letter":"m"},{"letter":"n"},{"letter":"ń"},{"letter":"o"},{"letter":"ó"},{"letter":"p"},{"letter":"q"},{"letter":"r"},{"letter":"s"},{"letter":"ś"},{"letter":"t"},{"letter":"u"},{"letter":"v"},{"letter":"w"},{"letter":"x"},{"letter":"y"},{"letter":"z"},{"letter":"ź"},{"letter":"ż"}];

class ImageViewer extends React.Component {
    constructor() {
        super();
        this.state = {
            ArrayIndexStart: 0,
            ArrayIndexEnd: 8
        };
        this.handleButtonClick = this.handleButtonClick.bind(this);
    }

    handleButtonClick(increment) {
        this.setState(prevState => {
            if (prevState.ArrayIndexStart + increment >= 8 && 
                prevState.ArrayIndexEnd + increment <=
                ObjectArray.length) {
                return {
                    ArrayIndexEnd: prevState.ArrayIndexEnd + increment,
                    ArrayIndexStart: prevState.ArrayIndexStart + increment
                };
            } else if (increment < 0) {
                return {
                    ArrayIndexEnd: 8,
                    ArrayIndexStart: 0
                };
            } else {
                return {
                    ArrayIndexEnd: ObjectArray.length,
                    ArrayIndexStart: ObjectArray.length - increment
                }
            }
        });
    }
    render() {
     return(
         
         <div>
         <button onClick={() => this.handleButtonClick(-8)}>left</button>     
         <button onClick={() => this.handleButtonClick(8)}>right</button>
         <div>
                   <p>{this.state.ArrayIndexStart}</p>
                    <p>{this.state.ArrayIndexEnd}</p>
        </div>

        {ObjectArray.slice(this.state.ArrayIndexStart, this.state.ArrayIndexEnd
         ).map(obj => obj.letter)
         }
        </div>
     )


     
    }}
    ReactDOM.render(
    <ImageViewer  />,
    document.getElementById('root')
    )
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
 <div id="root"></div>

Upvotes: 2

larv
larv

Reputation: 938

No a return statement ends where the return is made, so you can't return twice and hope to see both, the render function will stop after the first return.

An easy .map() function to iterate and return each object to your render function, this will be your golden key here. consider this code:

render() {
    return <div>
        <button onClick={() => this.handleButtonLeft(this.state.ArrayIndexStart - 8, this.state.ArrayIndexEnd - 8)}>left</button>     
        <button onClick={() => this.handleButtonRight(this.state.ArrayIndexStart + 8, this.state.ArrayIndexEnd + 8)}>right</button>
        <div>
            <p>{this.state.ArrayIndexStart}</p>
            <p>{this.state.ArrayIndexEnd}</p>
        </div>
        <div>
            {ObjectArray.slice(this.state.ArrayIndexStart, this.state.ArrayIndexEnd).map(item => {
                return <p>{item.letter}</p>
            })}
        </div>
    </div>
}

Upvotes: 1

Related Questions