Search code examples
javascriptreactjsreact-hooks

React Hooks: Best practise in passing state between children of hook component


I'd like to access an object set to a child component's state, and pass it to its sibling component:

  • Child 1 (TextDetection) is a react-dropzone component that receives an image file, which is passed to an OCR API.
  • Child 1 will then receive the transcription from the API and set that returned transcription to its local state.
  • Child 2 (TextTable) needs to access this same object, where it will be displayed.

Child 1

function TextDetection({ textToParent }) {
  const [data, setData] = useState([])

  const uploadImage = async (image) => {
    var formData = new FormData();
    formData.append('photo', image[0])
    const response = await fetch('/upload', {
      method: "post",
      body: formData
    })
    const body = await response.json();
    setData(body.split(/\r?\n/))
    textToParent(data)
  }

  return (
    <Grid container className="App" direction="column" alignItems="stretch">
      <Container id="dropzoneContainer">
        <Card variant="outlined">
          <Dropzone uploadImage = {uploadImage}/>
        </Card>
      </Container>
    </Grid>
  );
}

The intention is the make this transcription available to the sibling component. As you can see in the snippet below, I am currently accessing the child's state by passing a callback to it via props.

Parent

export default function App() {
  const [text, setText] = useState([])

  function handleChange(newText) {
    setText(newText)
  }
  
  return (
    <div>
      <Grid item xs={12} sm={4}>
        <TextDetection textToParent={handleChange}/>
      </Grid>
      <Grid item xs={12} sm={8}>
        <TextTable segments={text}/>
      </Grid>
    </div>
  );
}

Passing a callback to a child to access its state doesn't align with the concept of Lifting State Up. Is there a means of applying that principle in this case, given that the TextDetection child is receives the object set to state?


Solution

  • The TextDetection component should handle everything related to OCR, which includes parsing the text. However, it shouldn't have a local state. When done parsing it should call textToParent.

    function TextDetection({ textToParent }) {
      const uploadImage = async (image) => {
        var formData = new FormData();
        formData.append('photo', image[0])
        const response = await fetch('/upload', {
          method: "post",
          body: formData
        })
        const body = await response.json();
        textToParent(body.split(/\r?\n/));
      }
    
      return (
        <Grid container className="App" direction="column" alignItems="stretch">
          <Container id="dropzoneContainer">
            <Card variant="outlined">
              <Dropzone uploadImage = {uploadImage}/>
            </Card>
          </Container>
        </Grid>
      );
    }
    

    The parent should hold the state created by TextDetection, and supply it to the TextTable (and any other child that needs it).

    export default function App() {
      const [text, setText] = useState([])
      
      return (
        <div>
          <Grid item xs={12} sm={4}>
            <TextDetection textToParent={setText}/>
          </Grid>
          <Grid item xs={12} sm={8}>
            <TextTable segments={text}/>
          </Grid>
        </div>
      );
    }