react + redux (with toolkit) + typescript で
{
Name: string
Address: string
}
のようなhash を配列としてもたせたデータをUI上から入力、出力するような
画面を作成中ですが、以下のエラーでうまく動かない状態です。。。
TypeScript error in App.tsx(13,21): Parameter 'data' implicitly has an 'any' type. TS7006
App.tsx 内の
const onSubmit = (data) => { dispatch(setAddresses(data.addressbook)) }
にて"data" の型が指定されていないことでのエラーというのは
わかっているのですが、本件であればどのように
指定すればよいのかがわからず・・・。
※いくつかの参考文献をもとにこのように書いてしまってます・・・
以下のようなコードですが原因や修正点など
わかる方いらっしゃいましたらご教授ください。。。
- index.tsx
import React from "react"; import ReactDOM from "react-dom"; import './index.css'; import App from "./App"; import * as serviceWorker from './serviceWorker'; import { Provider } from "react-redux"; import { store } from './store'; ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById("root") ); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: https://bit.ly/CRA-PWA serviceWorker.unregister();
- App.tsx
import React from "react" import { useDispatch, useSelector } from "react-redux" import { useForm } from 'react-hook-form' import { setAddresses } from './rootSlice' import { RootState } from './rootReducer' import './common.css' const App = () => { const dispatch = useDispatch(); const addressbook = useSelector((state: RootState) => state.addressbook) const { register, handleSubmit } = useForm(); const onSubmit = (data) => { dispatch(setAddresses(data.addressbook)) } const addAddress = () => { newAddress = { Name: "", Address: "" } addressbook.push(newAddress) dispatch(setAddresses(addressbook)) } return ( <form onSubmit={handleSubmit(onSubmit)}> <div className="div-block"> {addressbook.map((address, index) => { return ( <> <label>Name {index}</label> <input name={`address[${index}].Name`} ref={register} /> <label>Address {index}</label> <input name={`address[${index}].Address`} ref={register} /> </> ) })} <button type="button" onClick={addAddress}>+</button> <button type="submit" className="btn-square-shadow">Apply !</button> </div> <div> <hr></hr> {addressbook.map((address, index) => { return ( <> <div>Name {index}: {address.Name}</div> <div>Name {index}: {address.Address}</div> </> ) })} </div> </form> ); }; export default App;
- rootSlice.ts
import { createSlice } from '@reduxjs/toolkit' import { AddressBook } from './Types' type State = { addressbook: Addresses[] } const initialState: State = { addressbook: [ { Name: "", Address: "hoge-1-1-1" } ] } const rootSlice = createSlice({ name: 'addressbook', initialState, reducers: { setName: (state, action) => { state.Name = action.payload }, setAddressBook: (state, action) => { state.addressbook = action.payload }, addAddresses: (state, action) => { state.Addresses.push(`address${state.Addresses.length}`) } } }) export const reducer = rootSlice.reducer; export const { setAddresses, addAddresses } = rootSlice.actions
- store.ts
import { configureStore } from '@reduxjs/toolkit' import { reducer } from './rootSlice' export const store = configureStore({ reducer })
- rootSlice.ts
type Addresses = { Name: string Address: string } type State = { addressbook: Addresses[] } const initialState: State = { addressbook: [ { Name: "", Address: "hoge-1-1-1" } ] } const rootSlice = createSlice({ name: 'addressbook', initialState, reducers: { setAddressBook: (state, action) => { state.addressbook = action.payload }, addAddresses: (state, action) => { state.addressbook.push({Name: "", Address: ""}) } } }) export const reducer = rootSlice.reducer; export const { setAddressBook, addAddresses } = rootSlice.actions export default rootSlice
- rootReducer.ts
import { combineReducers } from '@reduxjs/toolkit' import rootSlice from './rootSlice' export const rootReducer = combineReducers({ addressbook: rootSlice.reducer }) export type RootState = ReturnType<typeof rootReducer> export default rootReducer
- Types.ts
export type Addresses = { Name: string Address: string }
- tsconfig.json
{ "compilerOptions": { "target": "es5", "lib": [ "dom", "dom.iterable", "esnext" ], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "strict": true, "forceConsistentCasingInFileNames": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "react" }, "include": [ "src" ] }
あなたの回答
tips
プレビュー