skip to Main Content

I’m doing a project and I’m using React Admin to make the admin interface and FireStore which is the database. My problem is that I would like to make a <ReferenceInput> and a <SelectInput> to select data entered in an <ArrayInput> and I can’t find how to do it.

Here is the code with the <ArrayInput>:

export const TeamsCreate = (props) => (
    <Create {...props} >
        <SimpleForm>
            <TextInput source='teamName'/>
            <ArrayInput source="teamMembers">
                <SimpleFormIterator inline >
                    <TextInput source='teamMembersName' placeholder='Name' helperText={false}/>
                    <TextInput source='teamMembersSurname' placeholder='Surname' helperText={false}/>
                    <RadioButtonGroupInput source="teamMembersRole" choices={[
                        { id: 'climber', name: 'climber' },
                        { id: 'judges', name: 'judges' },
                    ]} />
                </SimpleFormIterator>
            </ArrayInput>
        </SimpleForm>
    </Create>
);

And here is my code where I want to retrieve the data from the teamMembers Array (I would like to retrieve all the first names of the people who are judges)

export const SectorCreate = (props) => (
    <Create {...props} >
        <SimpleForm>
            <NumberInput source='Sector Number' />
            <RadioButtonGroupInput source="Sector Type" choices={[
                { id: 'boulder', name: 'boulder' },
                { id: 'difficulty', name: 'difficulty' },
                { id: 'speed', name: 'speed' },
            ]} />
      <ReferenceInput label="Judge" source="teamMembers" reference="Teams">
        <SelectInput optionText="teamMembers" />
      </ReferenceInput>
      </SimpleForm>
    </Create>
)

I’ve done quite a few tests, and so far I’m only getting [Object object] Screenshot with what the selection shows

I also tried it like this:

export const SectorCreate = (props) => (
    <Create {...props} >
        <SimpleForm>
            <NumberInput source='Sector Number' />
            <RadioButtonGroupInput source="Sector Type" choices={[
                { id: 'boulder', name: 'boulder' },
                { id: 'difficulty', name: 'difficulty' },
                { id: 'speed', name: 'speed' },
            ]} />
      <ReferenceInput label="Judge" source="teamMembers[teamMembersName]" reference="Teams">
        <SelectInput optionText="teamMembers[teamMembersName]" />
      </ReferenceInput>
      </SimpleForm>
    </Create>
)

If anyone has a solution I’d love it 🙂

2

Answers


  1. The issue here seems to be that ReferenceInput is trying to use an ArrayInput as its source, which leads to a mismatch of data types and expectations. The ReferenceInput field expects a single ID that it can use to look up a referenced record. However, teamMembers is an array, which doesn’t fit this expectation.

    In your case, it seems like you want to present a selection of ‘judges’ from the teamMembers array, which is a more complex scenario than ReferenceInput is built to handle. Unfortunately, React Admin doesn’t provide out-of-the-box support for handling this complex case.

    To handle this scenario, you’ll likely need to create a custom input component that:

    1. Retrieves the list of teams.
    2. Flattens the list of team members across teams.
    3. Filters by role to select only judges.
    4. Presents this list to the user for selection.

    The custom component can then be used in the form like a regular input component.

    However, keep in mind that Firestore does not support complex queries out of the box, and you may need to set up a Cloud Function to support this functionality.

    If you have a back-end server, it might be easier to handle this functionality on the back end, by creating an API endpoint that serves the flattened, filtered list of judges.

    This is a high-level example of how you might implement this custom component:

    import { useQuery } from 'react-query';
    
    // Fetch team data from Firestore.
    const fetchTeams = async () => {
      // Assuming you have initialized Firebase.
      const db = firebase.firestore();
      const snapshot = await db.collection('Teams').get();
      return snapshot.docs.map(doc => doc.data());
    };
    
    // Custom select input for judges.
    const JudgeSelectInput = (props) => {
      const { isLoading, error, data } = useQuery('teams', fetchTeams);
    
      if (isLoading) return 'Loading...';
      if (error) return 'An error has occurred: ' + error.message;
    
      const judgeChoices = data
        .flatMap(team => team.teamMembers)  // Flatten members.
        .filter(member => member.teamMembersRole === 'judges')  // Filter for judges.
        .map((judge, i) => ({ id: i, name: `${judge.teamMembersName} ${judge.teamMembersSurname}` }));  // Map to choices.
    
      return (
        <SelectInput {...props} choices={judgeChoices} />
      );
    };
    
    // Use the custom component.
    export const SectorCreate = (props) => (
      <Create {...props}>
        <SimpleForm>
          <NumberInput source='Sector Number' />
          <RadioButtonGroupInput source="Sector Type" choices={[
              { id: 'boulder', name: 'boulder' },
              { id: 'difficulty', name: 'difficulty' },
              { id: 'speed', name: 'speed' },
          ]} />
          <JudgeSelectInput label="Judge" source="judge" />
        </SimpleForm>
      </Create>
    );
    

    This example uses react-query to fetch data from Firestore. Replace fetchTeams with the actual way you fetch data from Firestore. The JudgeSelectInput component fetches all teams, flattens the list of members, filters for judges, and displays their names as choices. This component can then be used like any other input component.

    Note: Firestore querying may result in multiple network requests which could lead to increased billing, please consider this before using the above solution. Also, please install and import react-query in your project if you want to use it.

    npm install react-query
    
    Login or Signup to reply.
  2. since comment space was not enough addressing you other question "Thanks for your reply, I just have a question, react-query is returning the following error: No QueryClient set, use QueryClientProvider to set one I’m not sure how to initiate it in the project"

    QueryClient is a key part of react-query and it’s required to fetch, cache, synchronize and update server state in your React applications. To use it, you need to create a QueryClient and pass it to QueryClientProvider which should be added near the top of your React component tree.

    Here is how you can do it:

    First, import the necessary modules:

    import { QueryClient, QueryClientProvider } from 'react-query';
    

    Then create a QueryClient instance:

    const queryClient = new QueryClient();
    

    Finally, wrap your application (or at least the part that uses react-query) with QueryClientProvider:

    function App() {
      return (
        <QueryClientProvider client={queryClient}>
          {/* your application code here */}
        </QueryClientProvider>
      );
    }
    

    So, for example, if your root component looked like this before:

    function App() {
      return (
        <div className="App">
          <SectorCreate />
        </div>
      );
    }
    

    It would now look like this:

    function App() {
      const queryClient = new QueryClient();
    
      return (
        <QueryClientProvider client={queryClient}>
          <div className="App">
            <SectorCreate />
          </div>
        </QueryClientProvider>
      );
    }
    

    Now you will be able to use useQuery and other hooks from react-query in your components.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search