Home > database >  Changing values of nested interfaces with react hooks
Changing values of nested interfaces with react hooks

Time:03-06

I want to create an app that can update an imported nested config file (similar to swagger).

I created an json config file, and generated interfaces for it with the help of https://transform.tools/json-to-typescript

Then i import the original json config file, convert it to nested interface and pass it to the useState hook. The question is, how can i update single values from the app?

interfaces.ts

export interface Config {
  project_info: ProjectInfo
  modules: Module[]
}

export interface ProjectInfo {
  go_version: number
  name: string
  port: number
}

Homepage.tsx

import React, { useState, useReducer } from "react"
import { Module, Endpoint, UrlParam, Body, Config, ProjectInfo } from "../interfaces"
import Header from "./Header"

import json_config from "../../../config.json"

export const Homepage = () => {

  // used to filter the selected module
  const [currentlySelectedModule, setCurrentlySelectedModule] = useState("")

  // import json file and convert it to the nested interface
  const placeholder_config: Config = JSON.parse(JSON.stringify(json_config))

  // convert the imported json config to hook, that has the form of generated interface
  const [config, setConfig] = useState<Config>(placeholder_config)

  return (

          <div className="code py-14 grid gap-14 mb-30">
            <div>
              <div className="fs-7 fw-700">ProjectInfo</div>
              <div className="">
                <div>project_name :{config.project_info.name}</div>
                <div>
                  project_name:
                  <input
                    className="module__endpoint_value_input endpoint__input_url"
                    value={config.project_info.name}
                    type="text"
                    // onChange={(e) => setConfig((e) => (config.project_info.name = e.target.value))}
                    // onChange={(e) => setPlaceholderConfig(() => config.project_info.name = e.target.value)}
                  />
                </div>
                <div>go_version :{config.project_info.go_version}</div>
                <div>port :{config.project_info.port}</div>
              </div>
            </div>
          </div>
)
}

So i'm sending the whole config file down, but want to change nested interface values.

How can i update ProjectInfo.name in this case? Additionally, is useState the best hook for this case?

Help appreciated.

CodePudding user response:

There are two considerable mistakes in your code:

  • Callback passed to setConfig is not returning new config
  • State is mutated inside this callback

You can implement custom hook for your config like this:

import { useState } from 'react';

function useConfig(props) {
    const [config, setConfig] = useState(props.config);

    // Setter for config.project_info.name
    // for other properties there should be other setters
    const setProjectName = (name) => {
        setConfig((config) => {
            // Should copy every level of data structure manually
            ...config,
            project_info: {
                ...config.project_info,
                name
            }
        })
    }

    return {
        config,
        setProjectName
    }
}

And then use it in your component:

import json_config from "../../../config.json"

const Homepage = () => {
    const { config, setProjectName } = useConfig({ config: json_config });

    const handleNameChange = (event) => {
        setProjectName(event.currentTarget.value)
    }

    return (
        <input
            type="text"
            value={config.project_info.name}
            onChange={handleNameChange}
        />
    )
}

If you don't want to copy deep nested objects manually, you could use libraries like Immer, which allows you to write "mutating" code, but not actually mutate it. Other option is to use reactive state management library like MobX.

  • Related