使用来自另一个组件的 React 状态挂钩填充表

时间:2021-07-07 05:22:12

标签: reactjs typescript react-hooks

我正在构建一个带有许多钩子的表单,在提交时会填充表格。到目前为止,我在一个应用程序中拥有所有功能 - 一切 - 。

  const [id, setId] = React.useState<number>(0);
  const [firstName, setFirstName] = React.useState<string>('')
  const [middleName, setMiddleName] = React.useState<string>('')
  const [lastName, setLastName] = React.useState<string>('')
  const [birthDay, setBirthDay] = React.useState<number>(0)
  const [birthMonth, setBirthMonth] = React.useState<number | string>(0)
  const [birthYear, setBirthYear] = React.useState<number>(0)
  const [birthState, setBirthState] = React.useState<string>('')
  const [birthCounty, setBirthCounty] = React.useState<string>('')
  const [birthTown, setBirthTown] = React.useState<string>('')
  const [people, setPeople] = React.useState<any[]>([])

当提交表单时,最终状态钩子“people”会填充一个表。呈现表格的函数取决于 people 数组。每个钩子对应表中的一列(保存最后一个钩子,它填充表):


  const renderHeader = () => {
    let columns = [
      'id',
      'firstName',
      'middleName',
      'lastName',
      'birthDay',
      'birthMonth',
      'birthYear',
      'birthState',
      'birthCounty',
      'birthTown'
    ]
    if (people.length === 0) {
      return <h3>add people to the array to view table</h3>
    } else if (people.length > 0) {
      return columns.map((col: string, index: number) => {
        return <th key={index}>{col.toUpperCase()}</th>
      });
    }
  }
  const renderBody = () => {
    return people.map(({
      id,
      firstName,
      middleName,
      lastName,
      birthDay,
      birthMonth,
      birthYear,
      birthState,
      birthCounty,
      birthTown 
    }) => {
      return (
        <tr key={id}>
          <td>{id}</td>
          <td>{firstName}</td>
          <td>{middleName}</td>
          <td>{lastName}</td>
          <td>{birthDay}</td>
          <td>{birthMonth}</td>
          <td>{birthYear}</td>
          <td>{birthTown}</td>
          <td>{birthCounty}</td>
          <td>{birthState}</td>
        </tr>
      );
    });
  }

我想把 App 分成两个组件:一个 Form 组件和一个 Table 组件。如果我将它们分开,我不知道如何将人钩传递到桌子上。有没有不使用 Redux 的方法来做到这一点?

以下是所有代码供参考:

import React from 'react';
import './App.css';
import { states } from './states';
import getCounties from './getCounties';

function App() {
  const [id, setId] = React.useState<number>(0);
  const [firstName, setFirstName] = React.useState<string>('')
  const [middleName, setMiddleName] = React.useState<string>('')
  const [lastName, setLastName] = React.useState<string>('')
  const [birthDay, setBirthDay] = React.useState<number>(0)
  const [birthMonth, setBirthMonth] = React.useState<number | string>(0)
  const [birthYear, setBirthYear] = React.useState<number>(0)
  const [birthState, setBirthState] = React.useState<string>('')
  const [birthCounty, setBirthCounty] = React.useState<string>('')
  const [birthTown, setBirthTown] = React.useState<string>('')
  const [people, setPeople] = React.useState<any[]>([])
  
  
  const renderHeader = () => {
    let columns = [
      'id',
      'firstName',
      'middleName',
      'lastName',
      'birthDay',
      'birthMonth',
      'birthYear',
      'birthState',
      'birthCounty',
      'birthTown'
    ]
    if (people.length === 0) {
      return <h3>add people to the array to view table</h3>
    } else if (people.length > 0) {
      return columns.map((col: string, index: number) => {
        return <th key={index}>{col.toUpperCase()}</th>
      });
    }
  }
  const renderBody = () => {
    return people.map(({
      id,
      firstName,
      middleName,
      lastName,
      birthDay,
      birthMonth,
      birthYear,
      birthState,
      birthCounty,
      birthTown 
    }) => {
      return (
        <tr key={id}>
          <td>{id}</td>
          <td>{firstName}</td>
          <td>{middleName}</td>
          <td>{lastName}</td>
          <td>{birthDay}</td>
          <td>{birthMonth}</td>
          <td>{birthYear}</td>
          <td>{birthTown}</td>
          <td>{birthCounty}</td>
          <td>{birthState}</td>
        </tr>
      );
    });
  }

  const handleFirstNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setFirstName(e.target.value);
  }
  const handleMiddleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setMiddleName(e.target.value);
  }
  const handleLastNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setLastName(e.target.value);
  }
  const handleBirthYearChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setBirthYear(e.target.valueAsNumber);
  }
  const handleBirthMonthChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setBirthMonth(e.target.value);
  }
  const handleBirthDayChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setBirthDay(e.target.valueAsNumber);
  }
  const handleBirthTownChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setBirthTown(e.target.value);
  }
  const handleBirthCountyChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    setBirthCounty(e.currentTarget.value);
  }
  const handleBirthStateChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    setBirthState(e.currentTarget.value)
  }

  const onSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    setPeople(people => [...people, {
      id,
      firstName,
      middleName,
      lastName,
      birthDay,
      birthMonth,
      birthYear,
      birthState,
      birthCounty,
      birthTown
    }]);
    setId(id + 1);
    setFirstName('');
    setMiddleName('');
    setLastName('');
    setBirthDay(0);
    setBirthMonth('');
    setBirthYear(0);
    setBirthTown('');
    setBirthCounty('');
    setBirthState('');
  }

  return (
    <div className="App">
      <h1><code>Kyle's Genealogy Thing</code></h1>
      
      <table id="people">
        <thead>
          <tr>{renderHeader()}</tr>
        </thead>
        <tbody>
          {renderBody()}
        </tbody>
      </table> 
      
      <form onSubmit={onSubmit}>
        <div>
          <input
            required
            value={firstName}
            placeholder='first name'
            id='firstName'
            name='firstName'
            onChange={handleFirstNameChange}
          />
          <input
            value={middleName}
            placeholder='middle name'
            id='middleName'
            name='middleName'
            onChange={handleMiddleNameChange}
          />
          <input
            required
            value={lastName}
            placeholder="last name"
            onChange={handleLastNameChange}
          />
          </div>
        <div>
          <label htmlFor="birthDay">day of birth</label>
          <input
            name="birthDay"
            id="birthDay"
            type='number'
            min='1'
            max='31'
            onChange={handleBirthDayChange}
          />
          <label htmlFor='birthMonth'>month of birth</label>
          <input
            name='birthMonth'
            id='birthMonth'
            min='1'
            max='12'
            onChange={handleBirthMonthChange}
            />
          <label htmlFor='birthYear'>year of birth</label>
          <input
            name='birthYear'
            id='birthYear'
            type='number'
            value={birthYear}
            onChange={handleBirthYearChange}
          />
        </div>
        <div>
          <label>state of birth</label>
          <select
            value={birthState}
            placeholder='state of birth'
            onChange={handleBirthStateChange}>
            {states.map(state =>
              <option key={state} value={state}>
                {state}
              </option>
            )}
          </select>
          <label>county of birth</label>
          <select
            value={birthCounty}
            placeholder='county of birth'
            onChange={handleBirthCountyChange}>
            {getCounties(birthState)}
          </select>
          <label>town or city of birth</label>
          <input
            value={birthTown}
            placeholder='town or city of birth'
            onChange={handleBirthTownChange}
          />
        </div>
        <button>submit</button>
      </form> 
    </div>
  );
}

export default App;

我也写了一个供人使用的接口,但是我没能实现它……我想这可能是另一个问题,但这里是我写的接口供参考:

export interface Place {
  state: string;
  county: string;
  town: string;
}

export interface Date {
  day: number;
  month: number | string;
  year: number;
}

export interface Name {
  first: string;
  middle?: string;
  last: string;
}

export interface Person {
  id: number;
  name: Name;
  birthdate: Date;
  birthplace: Place;
}

export interface People extends Person {
  people: Person[]
}

更新:

我已将组件分为以下几部分: 表格

import React from 'react';
import { Person } from '../Person';
import getCounties from '../getCounties';
import { states } from '../states';

const blankPerson: Person = {
    id: 0,
    firstName: '',
    middleName: '',
    lastName: '',
    birthYear: 0,
    birthMonth: 0,
    birthDay: 0,
    birthState: '',
    birthCounty: '',
    birthTown: ''
}

interface Props {
    onSave: (person: Person) => void
}

export function Form({ onSave }: Props) {
    const [person, setPerson] = React.useState(blankPerson);

    return <>
        <form onSubmit={() => onSave(person)}>
            <div>
                <input
                    required
                    value={person.firstName}
                    placeholder='first name'
                    id='firstName'
                    name='firstName'
                    onChange={e => setPerson({ ...person, firstName: e.target.value })}
                />
                <input
                    value={person.middleName}
                    placeholder='middle name'
                    id='middleName'
                    name='middleName'
                    onChange={e => setPerson({ ...person, middleName: e.target.value })}
                />
                <input
                    required
                    value={person.lastName}
                    placeholder="last name"
                    onChange={e => setPerson({ ...person, lastName: e.target.value })}
                />
            </div>
            <div>
                <label htmlFor="birthDay">day of birth</label>
                <input
                    value={person.birthDay}
                    name="birthDay"
                    id="birthDay"
                    type='number'
                    min='1'
                    max='31'
                    onChange={e => setPerson({ ...person, birthDay: e.target.valueAsNumber })}
                />
                <label htmlFor='birthMonth'>month of birth</label>
                <input
                    value={person.birthMonth}
                    name='birthMonth'
                    id='birthMonth'
                    min='1'
                    max='12'
                    onChange={e => setPerson({ ...person, birthMonth: e.target.valueAsNumber })}
                />
                <label htmlFor='birthYear'>year of birth</label>
                <input
                    name='birthYear'
                    id='birthYear'
                    type='number'
                    value={person.birthYear}
                    onChange={e => setPerson({ ...person, birthYear: e.target.valueAsNumber })}
                />
            </div>
            <div>
                <label>state of birth</label>
                <select
                    value={person.birthState}
                    placeholder='state of birth'
                    onChange={e => setPerson({ ...person, birthState: e.target.value })}>
                    {states.map(state =>
                        <option key={state} value={state}>
                            {state}
                        </option>
                    )}
                </select>
                <label>county of birth</label>
                <select
                    value={person.birthCounty}
                    placeholder='county of birth'
                    onChange={e => setPerson({ ...person, birthCounty: e.target.value })}>
                    {getCounties(person.birthState)}
                </select>
                <label>town or city of birth</label>
                <input
                    value={person.birthTown}
                    placeholder='town or city of birth'
                    onChange={e => setPerson({ ...person, birthTown: e.target.value })}
                />
            </div>
            <button>submit</button>
        </form>

    </>
}

表格

import React from 'react';
import { Person } from '../Person';

interface Props {
    people: Person[]
}

export function Table({people}: Props) {
    return <table>{ people.map(person => <tr>
        <td>{person.id}</td>
          <td>{person.firstName}</td>
          <td>{person.middleName}</td>
          <td>{person.lastName}</td>
          <td>{person.birthDay}</td>
          <td>{person.birthMonth}</td>
          <td>{person.birthYear}</td>
          <td>{person.birthTown}</td>
          <td>{person.birthCounty}</td>
          <td>{person.birthState}</td>
    </tr>)}</table>
}

应用程序将它们捆绑在一起:

import React from 'react';
import './App.css';
import { Person } from './Person';
import {Form} from './Form/Form';
import {Table} from './Table/Table';

function App() {
const [people, setPeople] = React.useState<Person[]>([])
 function addPerson(person: Person) {
    setPeople([...people, person])
  }
return (
    <div className="App">
      <h1><code>Kyle's Genealogy Thing</code></h1>
      <Form onSave={addPerson}/>
      <Table people={people}/>
    </div>
  );
}

export default App;

但现在表格没有更新。

2 个答案:

答案 0 :(得分:1)

所以你在这里以三个主要组成部分结束。

  • 表格
  • Parent(包含表单和表格)

首先,如果您将一个人建模为自己的界面会容易得多。你会经常使用它。类似的东西:

export interface Person {
  id: number
  firstName: string
  middleName: string
  lastName: string
  // etc...
}

表格

现在你可以让一个表单组件接受一个函数作为一个 prop。此函数接受整个 Person 对象作为参数。这个表单组件并不关心这个函数做什么。它需要做的就是与完成它的人一起调用它。由父组件来提供这个功能。

interface Props {
  onSave: (person: Person) => void
}

const blankPerson: Person = {
  id: 0,
  firstName: '',
  middleName: '',
  lastName: '',
  // etc...
}

export function MyForm({ onSave }: Props) {
  const [person, setPerson] = React.useState(blankPerson);

  return <>
    {/* ... */}
    <form onSubmit={() => onSave(person)}>
      <input
        required
        value={person.firstName}
        placeholder='first name'
        id='firstName'
        name='firstName'
        onChange={e => setPerson({ ...person, firstName: e.target.value })}
      />
    </form>
  </>
}

注意现在代替:

setFirstName(e.target.value)

你这样做:

setPerson({ ...person, firstName: e.target.value })

设置字段有点麻烦,但少很多状态实例是值得的。


表格

现在表,这个很容易。它甚至不需要状态,只需要道具。

interface Props {
  people: Person[]
}

export function MyTable({ people }: Props) {
  return <table>{ people.map(person => <tr>...</tr> }</table>
}

最后是将它们联系在一起的父级。

您可以在此处保存人员列表。目标是提供让表单添加人员所需的管道(通过 setPeople),并将当前人员列表提供到表中(通过 people)。

换句话说,你的表单是你状态的“setter”,而表是同一状态的只读数据。

function EditablePeopleList() {
  const [people, setPeople] = useState<Person[]>([])

  function addPerson(person: Person) {
    setPeople([...people, person])
  }

  return (
    <div>
      <MyForm onSave={addPerson} />
      <MyTable people={people} />
    <div>
  )
}

重点是尽可能保持状态本地化。表格不需要知道表格的状态,表格也不需要知道表格的状态。但是有一个父组件提供数据和函数来将 people 作为道具更新到它的子组件。

答案 1 :(得分:0)

像这样在你的表格组件中将人们状态作为道具传递。

您的单独表格组件:

const Table = (props) => {
const {people} = props;

  const renderHeader = () => {
    let columns = [
      'id',
      'firstName',
      'middleName',
      'lastName',
      'birthDay',
      'birthMonth',
      'birthYear',
      'birthState',
      'birthCounty',
      'birthTown'
    ]
    if (people.length === 0) {
      return <h3>add people to the array to view table</h3>
    } else if (people.length > 0) {
      return columns.map((col: string, index: number) => {
        return <th key={index}>{col.toUpperCase()}</th>
      });
    }
  }
  const renderBody = () => {
    return people.map(({
      id,
      firstName,
      middleName,
      lastName,
      birthDay,
      birthMonth,
      birthYear,
      birthState,
      birthCounty,
      birthTown 
    }) => {
      return (
        <tr key={id}>
          <td>{id}</td>
          <td>{firstName}</td>
          <td>{middleName}</td>
          <td>{lastName}</td>
          <td>{birthDay}</td>
          <td>{birthMonth}</td>
          <td>{birthYear}</td>
          <td>{birthTown}</td>
          <td>{birthCounty}</td>
          <td>{birthState}</td>
        </tr>
      );
    });
  }

  return <table id="people">
        <thead>
          <tr>{renderHeader()}</tr>
        </thead>
        <tbody>
          {renderBody()}
        </tbody>
      </table> 
}

然后在你的主要组件中导入你的新表格组件:

<Table people={people} />