0

after few hours of trying to correct the code i'm coming here for help. the idea is to search for a character and show few details that i choose, and right now i simply can't get around this issue where i try to iterate on the death array and gather some information from there :

I'm working with this API : https://tibiadata.com/doc-api-v2/characters/

   import React ,{useEffect,useState}from 'react'
    import './App.css';
    import SearchCharacter from './SearchCharacter'
    import Death from './Death'

    function App() {

    const [currentPlayer,setCurrentPlayer] =useState([])
    const [playerDeath,setPlayerDeath] =useState([])
    const [search,setSearch] = useState("")
    const [query,setQuery] = useState('')

    const BASE_URL = `https://api.tibiadata.com/v2/characters/${query}.json`
    useEffect(() => {

      fetch(BASE_URL)
      .then(res =>res.json())
      .then(data=>{
        setCurrentPlayer(data.characters.data,...Object.keys(data.characters.data))
        setPlayerDeath(...data.characters.deaths,...Object.keys(data.characters.deaths))
      })
    }, [query])

    const updateSearch = e=>{
      setSearch(e.target.value)
    }

    const getSearch =e =>{
      e.preventDefault();
      setQuery(search)
      setSearch('')
    }

     return (
      <div>
          <h1>Tibia </h1>
          <form onSubmit ={getSearch}>
            <input placeholder="Enter Name" input ={search} onChange ={updateSearch}/>
            <button >Search</button>
          </form>
          <SearchCharacter currentPlayer ={currentPlayer}/>
          {playerDeath.map(death =>(<Death
              key = {death.death.reason} 
              reason = {death.death.reason}
              level = {death.death.level}
          />))}
       </div>

      )
     }

    export default App

Death Component :

import React from 'react'

 const Death =({reason,level}) =>{
    return (
        <div>
            <p>Player Death: Played died by {reason} at level : {level}</p>
        </div>
    )
}

export default Death;

Search Character Component :

import React from 'react'

const SearchCharacter =({currentPlayer}) =>{
    return (
        <div>
            <p>Player Name :{currentPlayer.name}</p>
            <p>Player Level :{currentPlayer.level}</p>
            <p>Player Vocation :{currentPlayer.vocation}</p>
        </div>
    )
}


export default SearchCharacter

what i'm basically trying to do is have all of the deaths of a character presented under "Player Death" but no matter the variation of the code i'm trying i keep on getting the same error :

data.map is not a function - i have tried adapting other solutions to my code but i can't make it seem to work. at some point i could show one death ,and if there were no deaths - the code would crash.

right now after playing a lot with my code it won't even show the Death component jsx .

any help will be appreciated!

3
  • 2
    because data is not an array Commented Feb 28, 2020 at 14:12
  • You can avoid this by adding a null check, data && data.length && data.map Commented Feb 28, 2020 at 14:19
  • The console says that this.props.ingredients.map is not a function. I know this error is pretty common, but I’m a beginner with react and not totally sure what’s going on. I feel like I did the right things to make my updateStatefulRecipes method work. Commented Jul 11, 2020 at 23:04

2 Answers 2

2

Change setPlayerDeath(...data.characters.deaths,...Object.keys(data.characters.deaths))

to setPlayerDeath([...data.characters.deaths,...Object.keys(data.characters.deaths)])

as is you are essentially doing setPlayerDeath(v1, v2, v3, v4, ...etc), when you want setPlayerDeath([v1, v2, v3, v4, ...etc])

Sign up to request clarification or add additional context in comments.

1 Comment

thank you! it's working but for some reason it also renders 3 additional rows (for example, there are 3 rows that are actually a part of the array and 3 who aren't) that are part of the "Death" component. other than that, perfect!
0
import React from 'react'
import ReactDOM from 'react-dom'
import ReactBootstrap from 'react-bootstrap'
import { ListGroup } from 'react-bootstrap'
import { ListGroupItem } from 'react-bootstrap'
import { Panel } from 'react-bootstrap'
import { ButtonGroup } from 'react-bootstrap'
import { Button } from 'react-bootstrap'
import { Modal } from 'react-bootstrap'



const recipes = [
        {
        "name" : "Baklava",
        "ingredients": ["Flower", "Baking soda", "Pistachios", "Honey", "Puff Pastry", "Love", "Wawa"],
        "image" : "http://assets.simplyrecipes.com/wp-content/forum/uploads/2008/02/baklava-horiz-a-640.jpg"
        },
        {
        "name" : "Chips N' Dip",
        "ingredients": ["Chips", "Dip"],
        "image" : "http://dinnerthendessert.com/wp-content/forum/uploads/2015/09/Chips-and-Guac-Small-680x453.jpg"
        }
];



//This requires a recipe props of food, ingredietns, and an optional image, and prints specifically the recipe data
//The component deals with the drop down part of every recipe along with the buttons
class CollapseableRecipe extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      open: false,
    };
  }
  render() {
    const title = (
      <div>
      <a className="panelHead"onClick={()=>this.setState({open: !this.state.open})}>{this.props.food}</a>
      <ButtonGroup className="add-delete">
        <Button bsStyle="success">Add to shopping list</Button>
        <Button bsStyle="danger">Delete Recipe</Button>
      </ButtonGroup>
      </div>
    );
     let ingredients = this.props.ingredients.map((item) => {
     return (<ListGroupItem key={item}>{item}</ListGroupItem>)
     });
     let style = {
        "width": "100%",
        "height": "100%",
        "borderColor": "rgb(42, 42, 42)",
        "borderWidth": "5px",
        "borderRadius": "10px",
        "marginBottom": "2%"
     };
    return (
      <div>
        <Panel collapsible expanded={this.state.open} header={title}>
        <div>
        <h1 className ="text-center">{this.props.food}</h1>
        {this.props.image &&
        <img src={this.props.image} style={style}></img>
        }
        <ListGroup>
        {ingredients}
        </ListGroup>
        </div>
        </Panel>
      </div>
    )
  }
};

class AddToList extends React.Component {
        constructor(props) {
                super(props);
                this.state=({
                    showModal: false
                });
        }
        handleClick() {
                this.setState({ showModal : true});
        }
        close() {
                this.setState({ showModal : false});
        }
        updateRecipes() {
                if ($('#title').val() && $('#ingredients').val()) {
                    let recipe = {
                        "name" : $('#title').val(),
                        "ingredients" : $('#ingredients').val()
                    };
                    if ($('#image').val()) {
                            recipe["image"] = $('#image').val();
                    }
                    
                    this.props.update(recipe);
                    this.close();
                }
                alert("Hold up! You gotta fill in the necessary boxes!");
        }
        render() {
        $('body').click(function (event) {
            if(!$(event.target).closest('#openModal').length && !$(event.target).is('#openModal')) {
                $(".modalDialog").hide();
            }
        });
        const myModal = (
        <Modal show={this.state.showModal} onHide={() => this.close()} bsSize="large" aria-labelledby="contained-modal-title-lg">
            <Modal.Header closeButton>
                <Modal.Title id="contained-modal-title-lg">Add a new recipe</Modal.Title>
            </Modal.Header>
                <Modal.Body>
                    <form>
                        <h3>Name of Dish</h3>
                         <input type="text" label="Recipe" placeholder="Recipe Name" id="title" />
                        <h3>Ingredients</h3>
                         <input type="textarea" label="Ingredients" placeholder="Enter Ingredients(commas to separate)" id="ingredients"/>
                        <h3>Image</h3>
                         <input type="textarea" label="Image" placeholder="Enter a URL to an image(optional)" id="image"/>
                    </form>
                </Modal.Body>
            <Modal.Footer>
                <Button bsStyle="success" id="addRec" onClick={()=> this.updateRecipes()}>Add Recipe</Button>
            </Modal.Footer>
      </Modal>
        );
        return (
                <div>
                <button onClick={()=> this.handleClick()} className="addThings">+</button>
                {myModal}
                </div>
        );
    }
}




class FullBox extends React.Component {
  constructor(props) {
    super(props);
    this.state = ({
      recipes:[
              {
              "name" : "Baklava",
              "ingredients": ["Flower", "Baking soda", "Pistachios", "Honey", "Puff Pastry", "Love", "Wawa"],
              "image" : "http://assets.simplyrecipes.com/wp-content/forum/uploads/2008/02/baklava-horiz-a-640.jpg"
              },
              {
              "name" : "Chips N' Dip",
              "ingredients": ["Chips", "Dip"],
              "image" : "http://dinnerthendessert.com/wp-content/forum/uploads/2015/09/Chips-and-Guac-Small-680x453.jpg"
              }
              ]
    });
      this.updateStatefulRecipes = this.updateStatefulRecipes.bind(this);
  }
  updateStatefulRecipes(recipe) {
    var newArr = this.state.recipes.concat(recipe);
    this.setState({
        recipes: newArr
    });
  }
  render() {
      let localRecipes = this.state.recipes.map((item) => {
         return <CollapseableRecipe key={item["name"]} food={item["name"]} ingredients={item["ingredients"]} image={item["image"]} />
      });
    return (
      <div>
        {localRecipes}
        <AddToList update={this.updateStatefulRecipes} recipes={this.state.recipes}/>
      </div>
    );
  }
};
ReactDOM.render(<FullBox />, document.getElementById('render-target'));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.