Catanzaro
Catanzaro

Reputation: 1380

Iterate on JSON and add buttons in React

I'm new on React and I have a problem in this code:

class Tests extends React.Component {

   async getTests() {
    var url = "http://127.0.0.1:100/getTestList"
    var buttons=[];
    const resp = await fetch(url);
    const data = await resp.json();

    for(let pkg in data){ 
        console.log(pkg);
        for (let cls in data[pkg]){
            console.log(cls);

            for (var i = 0; i < data[pkg][cls].length; i++) {
                let m= data[pkg][cls][i];
                buttons.push(<button value={cls}>{m}</button>);
            }
        }
    }

    return buttons;

}
render(){
    return(
        <div>
            <h4>Using .map()</h4>
            {this.getTests()}

        </div>
    )
}

}//fine classe

// ========================================

ReactDOM.render(
  <Tests />,
  document.getElementById('root')

);

I take correctly JSON Response from POST service, iterate correctly the JSON for to have an array of buttons with value and text but I have this error on Chrome:

Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead. in div (at src/index.js:95) in Tests (at src/index.js:108)

Some Tips? Thanks Regards

Upvotes: 0

Views: 1490

Answers (3)

user2063635
user2063635

Reputation: 214

this error is expected. From your code, getTest method returns an array of buttons. You cannot render an array or an object from a react component.

Instead you can

  1. make buttons a state variable,
  2. set state after you prepare a local array in your getTest method.
  3. use math method to render the button tag that you defined in array.

Upvotes: -1

Sohail Ashraf
Sohail Ashraf

Reputation: 10579

getTests() is an async function and it will always return a promise object. React is complaining because you are passing a promise object to the render method.

You could store the api response in a state, and set the state in getTests method. React will update the ui on state update.

Few changes in the code.

  1. Create state
  2. Run the getTests() in componentDidMount method.
  3. Loop over the values in render method.

Example:

class Tests extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            buttons: []
        }
    }
    async getTests() {
        var url = "http://127.0.0.1:100/getTestList"
        var buttons = [];
        const resp = await fetch(url);
        const data = await resp.json();
        for (let pkg in data) {
            console.log(pkg);
            for (let cls in data[pkg]) {
                console.log(cls);
                for (var i = 0; i < data[pkg][cls].length; i++) {
                    let m = data[pkg][cls][i];
                    buttons.push({ value: cls, displayName: m });
                }
            }
        }
       this.setState( () => {
            return {
                buttons
            }
        });
    }
    async componentDidMount() {
        await getTests();
    }
    render() {
        return (
            <div>
                <h4>Using .map()</h4>
                {this.state.buttons.map(button => <button key={button.value} value={button.value}>{button.displayName}</button>) }
            </div>
        )
    }
}//fine classe
// ========================================
ReactDOM.render(
    <Tests />,
    document.getElementById('root')
);

Or you could use a functional component for this.

const ButtonGroup = () => {
    const [buttons, setButtons] = useState([]);

    useEffect(() => {
        await getTests();
    }, []);

    async function getTests() {
        const url = 'http://127.0.0.1:100/getTestList';
        const resp = await fetch(url);
        const data = await resp.json();
        const buttonData = [];
        for (let pkg in data) {
            for (let cls in data[pkg]) {
                for (var i = 0; i < data[pkg][cls].length; i++) {
                    let m = data[pkg][cls][i];
                    buttonData.push({ value: cls, displayName: m });
                }
            }
        }
        setButtons(buttonData);
    }

    return (
        <div>
            <h4>Using.map()</h4>
            {buttons.map(button =>
                (
                    <button key={button.value} value={button.value}>{button.displayName}</button>
                )
            )}
        </div>
    );
}

export default ButtonGroup;

Upvotes: 1

norbitrial
norbitrial

Reputation: 15166

I would use a function component with useState and useEffect for this case. In useEffect you can fetch your data from the endpoint then using .map() you can represent your buttons from the array what you instantiate with useState.

Please see the following solution which might resolve the issue:

const Tests = () => {
   const [buttons, setButtons] = useState([]);

   useEffect(() => {      
      async getTests() {
         const url = 'http://127.0.0.1:100/getTestList';
         const resp = await fetch(url);
         const data = await resp.json();
         const fetchedButtons = [];

         for (let pkg in data) { 
            for (let cls in data[pkg]) {
                for (var i = 0; i < data[pkg][cls].length; i++) {
                    let m = data[pkg][cls][i];
                    fetchedButtons.push(<button value={cls}>{m}</button>);
                }
            }
         }

         setButtons(fetchedButtons);
      }

      getTests();   
   }, []);

   return <div>
       <h4>Using.map()</h4>
       {/* here mapping the buttons array */}
       {
          buttons && buttons.map(e => e);          
       }
   </div>
}

I hope this helps!

Upvotes: 1

Related Questions