简体   繁体   中英

Binding API Data from React Parent Component to Child Components

I'm new to React and am tripping over this issue.

Have read couple of tutorials and questions here to find out about how Parent & Child Components should communicate. However, I am unable to get the data to populate the fields + make it editable at the same time. I'll try explain further in code below:

Parent Component:

...imports...

export default class Parent extends Component {

   constructor(props) {
      this.state = {
         data: null
      };
   }

   componentDidMount() {
      API.getData()
         .then((response) => {
            this.setState({ data: response });
            // returns an object: { name: 'Name goes here' }
         })
   }

   render() {
      return (
          <Fragment>
              <ChildComponentA data={this.state.data} /> 
              <ChildComponentB data={this.state.data} />
          </Fragment>
      );
   }
}

Input Hook: (source: https://rangle.io/blog/simplifying-controlled-inputs-with-hooks/ )

import { useState } from "react";

export const useInput = initialValue => {
  const [value, setValue] = useState(initialValue);

  return {
    value,
    setValue,
    reset: () => setValue(""),
    bind: {
      value,
      onChange: event => {
        setValue(event.target.value);
      }
    }
  };
};

ChildComponent:* (This works to allow me to type input)

import { Input } from 'reactstrap';
import { useInput } from './input-hook';

export default function(props) {

  const { value, setValue, bind, reset } = useInput('');

  return (
    <Fragment>
       <Input type="input" name="name" {...bind} />
    </Fragment>
  );
}

ChildComponent Component: (Trying to bind API data - Input still editable but data is still not populated even though it is correctly received.. The API data takes awhile to be received, so the initial value is undefined)

import { Input } from 'reactstrap';
import { useInput } from './input-hook';

export default function(props) {

  const { value, setValue, bind, reset } = useInput(props.data && props.data.name || '');

  return (
    <Fragment>
       <Input type="input" name="name" {...bind} />
    </Fragment>
  );

}

ChildComponent Component: (Trying to use useEffect to bind the data works but input field cannot be typed..) I believe this is because useEffect() is trigged every time we type.. and props.data.name is rebinding its original value

import { Input } from 'reactstrap';
import { useInput } from './input-hook';

export default function(props) {

  const { value, setValue, bind, reset } = useInput(props.data && props.data.name || '');

  useEffect(() => {
     if(props.data) {
       setValue(props.data.name);
     }
  });

 return (
    <Fragment>
       <Input type="input" name="name" {...bind} />
    </Fragment>
  );

}

I can think of a few tricks like making sure it binds only once etc.. But I'm not sure if it is the correct approach. Could someone share some insights of what I could be doing wrong? And what should be the correct practice to do this.

To iterate, I'm trying to bind API data (which takes awhile to load) in parent, and passing them down as props to its children. These children have forms and I would like to populate them with these API data when it becomes available and yet remain editable after.

Thanks!

Basic way to create your Parent/Child Component structure is below, I believe. You don't need a class-based component for what you are trying to achieve. Just add an empty array as a second argument to your useEffect hook and it will work as a componentDidMount life-cycle method.

Parent component:

import React, {useState, useEffect} from 'react';

export default const Parent = () => {
  const [data, setData] = useState({});
  const [input, setInput] = useState({});

  const inputHandler = input => setInput(input);

  useEffect(() => { 
    axios.get('url')
      .then(response => setData(response))
      .catch(error => console.log(error)); 
  }, []);

  return <ChildComponent data={data} input={input} inputHandler={inputHandler} />;

};

Child Component:

import React from 'react';

export default const ChildComponent = props => {

  return (
    <div>
      <h1>{props.data.name}</h1>
      <input onChange={(e) => props.inputHandler(e.target.value)} value={props.input} />
    </div>
    );

};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM