zack_falcon
zack_falcon

Reputation: 4386

React: Updating / appending an array in state from a functional component

I'm trying to make a multi-step form with some caveats:

  1. There's a number input field that dictates how many upcoming dropdowns are displayed (i.e., if I put in 3, there would be 3 dropdowns below it).
  2. The dropdowns have a default value, randomized from a list - no dropdown can have the same value.
  3. The dropdowns can be edited by the user, but will warn the user if another dropdown has the same value.

I chose dropdowns because the values that they pull from (a JSON) will not change, although I'm not entirely sure it's a good idea. I've been able to do step 1 and 2, but I'm stuck on step 3.

The problem I'm having is that I could not save the initial randomized values from randomNpcList the dropdown into state (although at least, they are displayed just fine), and changing the value of the dropdowns doesn't seem to save it into the array in state either (thus, my check code never really runs).

Here's my code sandbox: https://codesandbox.io/s/focused-rain-vbr1mi

Here's my code for the form:

export default class SeasonalDataForm extends Component {
    state = {
        step: 1,
        hasLoadedJson: false,
        duration: 4
        total_npcs: 5,
        selected_npcs: [],
    }

    containsObject(obj, list) {
        for (const element of list) {
            if (element === obj) {
                return true;
            }
        }
        return false;
    }

    async componentDidMount() {
        /* Load data from JSON
        localStorage.setItem(`persistentJsonData`, JSON.stringify(jsonData));
        this.setState({ hasLoadedJson: true });*/
    }
    
    prevStep = () => { // go back to previous step }

    nextStep = () => { // proceed to the next step }

    handleChange = input => e => {
        switch (input) {
            case 'selected_npc':
                const jsonData = JSON.parse(localStorage.getItem(`persistentJsonData`));
                const npcFromDropdown = jsonData.NPCs.find((npc) => parseInt(npc.id) === parseInt(e.target.value));

                if (!this.containsObject(npcFromDropdown, this.state.selected_npcs)) {
                    this.setState({ selected_npcs: [...this.state.selected_npcs, npcFromDropdown] });
                } else { alert("NPC already selected.");}
                break;
            default:
                break;
        }
    }

    render() {
        const { step, hasLoadedJson } = this.state;
        const { duration, selected_region, total_npcs, selected_npcs, } = this.state;
        const values = { duration, selected_region, total_npcs, selected_npcs, };

        if (hasLoadedJson) {
            switch (step) {
                case 2:
                    return (
                        <NpcSelector nextStep={this.nextStep} prevStep={this.prevStep} handleChange={this.handleChange} values={values} defaults={MIN_MAX_DEFAULTS} />
                    );
                default:
            }
        } else {
            return (
                <div>Data loading...</div>
            );
        }
    }
}

And here's the code for the NPC selector:

const NpcSelector = ({ nextStep, prevStep, handleChange, values, defaults }) => {
    const jsonData = JSON.parse(localStorage.getItem(`persistentJsonData`));
    const weekStructure = jsonData.Season_Length.find((week) => parseInt(week.week_structure) === parseInt(values.duration));
    const totalNpcs = values.total_npcs;
    const npcList = jsonData.NPCs;
    let randomNpcList = [];

    let i = 0;
    while (i < totalNpcs) {
        let index = Math.floor(Math.random() * npcList.length);
        let npc = npcList[index];
        if (containsObject(npc, randomNpcList)) {
            continue;
        } else {
            i++;
            randomNpcList.push(npc);
        }
    }

    return (
        <div>
            <h2>NPCs</h2>
            <form>
                <label>
                    Total NPCs:
                    <input type="number" name="npcs" min={defaults.NPCS.MIN} max={defaults.NPCS.MAX} defaultValue={weekStructure.npcs} onChange={handleChange('total_npcs')} />
                </label>
                Currently selected randomized NPCs:
                {
                    randomNpcList && randomNpcList.map((npc, index) => {
                        return (
                            <div key={`selected_npc_${index}`}>
                                <select name={`npc_selected_${index}`} value={npc.id} onChange={handleChange('selected_npc')} >
                                    {
                                        npcList && npcList.map((value, index) => {
                                            return (<option key={index} value={value.id}>{value.name}</option>);
                                        })
                                    }
                                </select>
                            </div>
                        )
                    })
                }
            </form>
        </div>
    )
}

export default NpcSelector

And the JSON file would look something like this:

{
    "Season_Length": [{"week_structure": 4,"npcs": 5,}],
    "NPCs": [
        {"id": 1,"name": "Akara"},
        {"id": 2,"name": "Charsi"},
        {"id": 3,"name": "Deckard"},
        {"id": 4,"name": "Flavie"},
        {"id": 5,"name": "Gheed"},
        {"id": 6,"name": "Kashya"},
        {"id": 7,"name": "Warriv"}
    ],
}

Ideally, for example, if I put in the number of NPCs as 4, the system would create 4 dropdowns, each with a random, non-repeating NPC (ex: Akara, Charsi, Flavie, Kashya). This would be saved in the state, and checked against any changes (ex: changing Kashya into Warriv is okay, changing Kashya into Akara would trigger an alert).

Any advice? Thanks.

Upvotes: 1

Views: 39

Answers (1)

Anu
Anu

Reputation: 389

I have made some minimal changes in SeasonalDataForm and NPCSelector, please find the forked sandbox. Let me know if this is what you wanted, or do you expect something else.

https://codesandbox.io/s/sharp-hodgkin-6gj86l?file=/src/SeasonalDataGenerator/SeasonalDataForm.js

Thanks, Anu

Upvotes: 1

Related Questions