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.