Home > Net >  React - use state on an empty array
React - use state on an empty array

Time:10-15

I'm really confused on how to do useState in React on an empty array

import "./styles.css";
import text from "./text.js";
import React, { useState } from "react";

export default function App() {
  let allTheWords = text.split(" ");
  let countData = [];
  let theWordCount = {};
  const createTable = () => {
    for (const num of allTheWords) {
      theWordCount[num] = theWordCount[num] ? theWordCount[num]   1 : 1;
    }
    for (const property in theWordCount) {
      countData.push(
        <tr key={property}>
          <td>{property}</td>
          <td>{theWordCount[property]}</td>
        </tr>
      );
    }
  };
  createTable();
  const sortWords = () => {
    theWordCount = Object.entries(theWordCount)
      .sort(([, a], [, b]) => a - b)
      .reduce((r, [k, v]) => ({ ...r, [k]: v }), {});
    console.log("OKAY I THINK WE SORTED: "   JSON.stringify(theWordCount));
    createTable();
  };
  return (
    <div>
      <button onClick={sortWords}>SORT WORDS</button>
      <table>
        <tbody>
          <tr>
            <th>Word</th>
            <th>Count</th>
          </tr>
          {countData}
        </tbody>
      </table>
    </div>
  );
}

I need to somehow do useState on the variable countData so I can update the DOM when theWordCount changes.

https://reactjs.org/docs/hooks-state.html

I read through the above and got to this

import "./styles.css";
import text from "./text.js";
import React, { useState } from "react";

export default function App() {
  let allTheWords = text.split(" ");
  let countData = [];
  let theWordCount = {};
  countData = useState(0);
  const createTable = () => {
    for (const num of allTheWords) {
      theWordCount[num] = theWordCount[num] ? theWordCount[num]   1 : 1;
    }
    for (const property in theWordCount) {
      countData.push(
        <tr key={property}>
          <td>{property}</td>
          <td>{theWordCount[property]}</td>
        </tr>
      );
    }
  };
  createTable();
  const sortWords = () => {
    theWordCount = Object.entries(theWordCount)
      .sort(([, a], [, b]) => a - b)
      .reduce((r, [k, v]) => ({ ...r, [k]: v }), {});
    console.log("OKAY I THINK WE SORTED: "   JSON.stringify(theWordCount));
    createTable();
  };
  return (
    <div>
      <button onClick={sortWords}>SORT WORDS</button>
      <table>
        <tbody>
          <tr>
            <th>Word</th>
            <th>Count</th>
          </tr>
          {countData}
        </tbody>
      </table>
    </div>
  );
}

But the DOM doesn't update or do anything or give me any errors.

In this context how do you make countData converted into a state in the function?

CodePudding user response:

To create an array called countData in your component's state, the syntax would look something like this:

const [countData, setCountData] = useState([]);

Now, as far as I'm aware, it's considered bad practice to "mutate" (aka edit) state directly. This is what the setCountData function returned by useState is for.

So, instead of pushing to the array directly with countData.push(), you would want to make a clone of this array, and use setCountData() to update the state with the cloned array.

For example,

// This will be our "cloned" array that we will use in setCountData()    
let newArray = countData;

// Push to this array instead of countData
newArray.push('something here');

// Then, update the countData array using setCountData() with this array we just created
setCountdata(newArray);

CodePudding user response:

const [countData, setCountData] = useState([]);

//When you need modify array:
let row = '<tr>....</tr>';
setCountData((current)=>[...current, row])

CodePudding user response:

You should

  • Keep the state value consistent, to make manipulation of it easy - for example, if you use useState(0), further uses of that state variable should be a number. But that's not what you have here - the useState argument should correspond to the type of state value you're using

  • React components shouldn't be put into state. Instead, state should be composed of (usually serializable) plain JavaScript values whenever possible, and then transformed into JSX when rendering.

  • useState returns an array containing two values: the state as the first value, and the state setter as the second value. Call the state setter to update the state. Since it looks like an array of objects most closely resembles the sort of state you'd want to work with, this:

    let countData = [];
    let theWordCount = {};
    countData = useState(0);
    

    should be changed to

    const [words, setWords] = useState([])
    

    where the words array contains objects with word and count properties.

    Or, have a function that transforms the initial props into the array structure, then passes that to useState.

const textToWords = (text) => {
  const wordsMap = {};
  for (const word of text.split(' ')) {
    wordsMap[word] = (wordsMap[word] || 0)   1;
  }
  return Object.entries(wordsMap)
    .map(([word, count]) => ({ word, count }));
};
const [wordsArr, setWordsArr] = useState(() => textToWords(text));
// And when rendering, instead of {countData}:
{wordsArr.map(({ word, count }) => (
  <tr>
    <td>{word}</td>
    <td>{count}</td>
  </tr>
))}

To set the state, sort the array (after cloning it, to avoid mutation), then call setWordsArr.

const sortWords = () => {
  setWordsArr(
    wordsArr
      .slice()
      .sort((a, b) => a.count - b.count)
  );
};
  • Related