0

What I've tried and my issue I started with creating an external function and running it through the onClick... this works partly as it sends the alerts on click. See the services page on test.ghostrez.net. Click the small images to trigger the alerts that show which if statement, thestate.active:value, and the state.id:value. So I know the correct statements are being triggered.

My problem is I keep having state[i].setState is not a function returned rather than the state being set as intended.

I have placed the function internally and externally to the class Player and it returned the same issue.

I converted the function to an internal arrow function as suggested HERE.

I converted it to a const changeActiveField = () => {stuff in here}

I attempted to bind it const changeActive = changeActiveField.bind(this) *as suggested HERE and HERE

Each attempt returning the same Error enter image description here

this is what the debug console returns enter image description here

Here is my current function its process > 1. if the active object in state has the same id as image clicked - do nothing, 2. if the active object has a different id to the image clicked setState active:value to false then come back and find the object with the id === id of the image clicked and setState active:true from false.

function changeActiveField(im, state) {
  console.log(state);
  for (var i = 0; i < state.length; i++) {
    if (state[i].active === true && state[i].id === im) {
      return alert("if " + state[i].active + " " + state[i].id);
    } else if (state[i].active === true && state[i].id !== im) {
      alert(" elseif set false " + state[i].active + " " + state[i].id);
      state[i].setState(false);
    } else if (state[i].id === im) {
      alert("elseif make true " + state[i].active + " " + state[i].id);
      state[i].setState({ active: true });
      return;
    } else {
      return alert("Nope");
    }
  }
}

changeActiveField is called here

 <div className="thumbs">
          {this.state.ids.map((i) => (
            <>
              <Image
                className="carouselitem"
                rounded
                onClick={() => changeActiveField(i.id, this.state.ids)}
                src={"http://img.youtube.com/vi/" + i.id + "/hqdefault.jpg"}
                size="small"
              />

              <h2>
                {i.id} {i.active ? "true" : "false"}
              </h2>
            </>
          ))}
        </div>

No joke I've been trying to resolve this for 4 days now. I'm stumped.

  • Please post the code that calls `changeActiveField` – Drag13 Sep 20 '21 at 06:49
  • @Drag13 Added code to bottom –  Sep 20 '21 at 07:36
  • `setState` is a method of a React `Component` class, not any random object. You must call `setState` on an instance of a class. Typically one does this using `this.setState` from a method of the component, ensuring `this` refers to the component. – Heretic Monkey Sep 20 '21 at 14:57

2 Answers2

0

It appears that you are trying to setState on an individual id, but what you are actually doing is trying to call id.setState

From the code you supplied, each id looks basically like this:

{active: //true/false, id: //some Int}

but in reality your code is looking for this...

{active: //true/false, id: //some Int, setState: () => //do something here}

You'll need to handle how to find your specific id object in that array of ids, and then update your full state with the current state AND the modification you are making.

EDIT://my fault, wasn't thinking.

I would recommend making a copy of your state array in a new variable, then mapping through that new array variable making your mutations. Then set your state based on that new array objects...

let newIdArr = this.state.ids

newIdArr.map(id => //do your stuff here...)

this.setState({...this.state, ids: newIdArr}) 

Lastly, when you setState(false) you are overwriting ALL your state to where it will be just false, losing all your ids along the way.

TR3
  • 327
  • 3
  • 6
0

This is the end product of too many days pulling my hair out... but it works now and hopefully, it helps someone else. (full component code last)

I used an anonymous function in the Image that is being rendered. This finds and updates the object in the this.state array, first, it finds the ids that don't match the value passed in from the "carouselitem" and updates their active values to false, then it finds the id that matches the value passed in and updates it to true.

The old function changeActiveField is now

onClick={() => {
                  this.setState((prevState) => ({
                    ids: prevState.ids.map((ob) =>
                      ob.id !== i.id
                        ? { ...ob, active: false }
                        : { ...ob, active: true }
                    ),
                  }));
                }}

I have also moved my firstActiveId into the class. This finds the array object with active: true and returns the id value which is placed in the activevid to display and play the appropriate video.

firstActiveId = () => {
    for (var i = 0; i < this.state.ids.length; i++) {
      if (this.state.ids[i].active) {
        return this.state.ids[i].id;
      }
    }
  };

The firstActiveId is used like this to provide playback.

<div className="activevid">
          <Embed
            active
            autoplay={true}
            color="white"
            hd={false}
            id={this.firstActiveId(this.state.ids)}
            iframe={{
              allowFullScreen: true,
              style: {
                padding: 0,
              },
            }}
            placeholder={
              "http://img.youtube.com/vi/" +
              this.firstActiveId(this.state.ids) +
              "/hqdefault.jpg"
            }
            source="youtube"
          />
        </div>

TIP: don't over-complicate things like I do

Full Component

import React, { Component } from "react";
import { Embed, Image } from "semantic-ui-react";
import "./Player.css";

export default class Player extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      ids: [
        {
          id: "iCBvfW08jlo",
          active: false,
        },
        {
          id: "qvOcCQXZVg0",
          active: true,
        },
        {
          id: "YXNC3GKmjgk",
          active: false,
        },
      ],
    };
  }
  firstActiveId = () => {
    for (var i = 0; i < this.state.ids.length; i++) {
      if (this.state.ids[i].active) {
        return this.state.ids[i].id;
      }
    }
  };

 

  render() {
    return (
      <div className="carouselwrap">
        <div className="activevid">
          <Embed
            active
            autoplay={true}
            color="white"
            hd={false}
            id={this.firstActiveId(this.state.ids)}
            iframe={{
              allowFullScreen: true,
              style: {
                padding: 0,
              },
            }}
            placeholder={
              "http://img.youtube.com/vi/" +
              this.firstActiveId(this.state.ids) +
              "/hqdefault.jpg"
            }
            source="youtube"
          />
        </div>
        <div className="thumbs">
          {this.state.ids.map((i) => (
            <>
              <Image
                className="carouselitem"
                rounded
                onClick={() => {
                  this.setState((prevState) => ({
                    ids: prevState.ids.map((ob) =>
                      ob.id !== i.id
                        ? { ...ob, active: false }
                        : { ...ob, active: true }
                    ),
                  }));
                }}
                src={"http://img.youtube.com/vi/" + i.id + "/hqdefault.jpg"}
                size="small"
              />
            </>
          ))}
        </div>
      </div>
    );
  }
}

  • Little simple refactor for you...`.find()` returns the first element that satisfies the condition. Be aware that you might be **assuming your array is always sorted** by id. `firstActiveId = this.state.ids.find(id => id.active)` – TR3 Sep 22 '21 at 17:42