Home > Mobile >  How to reduce and setState of an array of objects into one object
How to reduce and setState of an array of objects into one object

Time:05-03

I am trying to create a form to capture values. My data will come in looking like this:

const dummyData = [
  { question: "what is your name?", isActive: true },
  { question: "what is your age?", isActive: true },
  { question: "what is your dob?", isActive: true }
];

I need to get values of all the inputs for the above questions.

I am trying to set formData to a state that looks like this

const formData = {
    whatIsYourName: '', 
    whatIsYourAge: '', 
    whatIsYourDob?: ''
};

import "./styles.css";
import { useState, useEffect } from "react";

const dummyData = [
  { question: "what is your name?", isActive: true },
  { question: "what is your age?", isActive: true },
  { question: "what is your dob?", isActive: true }
];

const App = () => {
  const [formData, setFormData] = useState({});

  const converDataToObject = () => {
//do something
};

  useEffect(() => {
    converDataToObject();
  }, []);

  return <div className="App">test</div>;
};

export default App;

CodePudding user response:

Try to use this helper function to generate the desider object:

const dummyData = [
    { question: "what is your name?", isActive: true },
    { question: "what is your age?", isActive: true },
    { question: "what is your dob?", isActive: true }
  ];
  
const converDataToObject = () => {
    const result = {}
  for (const obj of dummyData) {
    result[generateKey(obj.question)] = ""
  }
    return result;
};

// Generate a key from the given question
const generateKey = (question) => {
  // Split the question by ` ` after removing the `?`
  const keySplit = question.toString().replaceAll("?", "").split(" ");
  // Capitalize each substring after the first one
  // and join the result into a single value
    return keySplit.map((s, idx) => {
        if (idx === 0) return s;
      return capitalize(s);
    }).join("");
}

// Helper function, capitalize a string
const capitalize = (s) => {
    return s.charAt(0).toUpperCase()   s.slice(1).toLowerCase();
}

console.log(converDataToObject())

You will just need to set the formData with:

setFormData(convertDataToObject())

CodePudding user response:

You may want to keep everything together in state.

whatisyourname: { question: 'What is your name?', isActive: true }  

and then add a new value property to the state when a corresponding input is updated.

That said it would be better (if you have control over the incoming data) to add a name property to the object, and use that instead, since it's much more meaningful.

{ name: 'name', question: 'What is your name?', isActive: true },

becomes this when it's converted, and you don't have to do any unnecessary find/replace on the key name.

name: { name: 'name', question: 'What is your name?', isActive: true },

const { useEffect, useState } = React;

// Small conversion function that
// creates an object with nested objects
function convertData(data) {

  const out = {};
  
  for (const obj of data) {
    const key = obj.question
      .toLowerCase()
      .replaceAll(/[ ?] /g, '');
    out[key] = obj;
  }
  
  return out;

}

function Example({ data }) {

  const [ form, setForm ] = useState({});

  // Create the state using the conversion function
  useEffect(() => {
    const converted = convertData(data);
    setForm(converted);
  }, []);

  // A function to create the form
  // It maps over each property, and creates
  // the labels and inputs
  function createForm(form) {
    return Object.entries(form).map(([key, obj]) => {
      return (
        <div className="input">
          <label>{obj.question}</label>
          <input
            key={key}
            name={key}
            value={obj.value}
          />
        </div>
      );
    });
  }

  // A function to handle changes to any
  // of the input values. Because we're using event
  // delegation we first check that the element that was
  // changed was an input, then we extract the name and value
  // from it, and set the new form, updating the value in the object
  function handleChange(e) {
    if (e.target.matches('input')) {
      const { name, value } = e.target;
      setForm({
        ...form,
        [name]: { ...form[name], value }
      });
    }
  }

  useEffect(() => console.log(form), [form]);

  // Use event delegation on the div
  // to check for changes in the child elements
  return (
    <div onChange={handleChange}>
      {createForm(form)}
    </div>
  );

}

const data = [
  { question: 'What is your name?', isActive: true },
  { question: 'What is your age?', isActive: true },
  { question: 'What is your dob?', isActive: true }
];

ReactDOM.render(
  <Example data={data} />,
  document.getElementById('react')
);
label { margin-right: 1em; }
.input { margin-bottom: 0.5em; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Additional documentation

  • Related