简体   繁体   中英

Sharing data between React components

I have a search form in React that performs an API call and saves the data called nameservers in-state using the useState() React hook.

I'm trying to determine how I can pass this data to another component so the data can be used for rendering elsewhere on the page.

Currently, I'm just outputting the data in my return block which appears below the search form.

import React, { useState } from "react";
import { Spinner, Form, Button, Container, Col } from "react-bootstrap";
import { FaSearch } from "react-icons/fa";

const SearchForm: React.FC = () => {
  const [domain, setDomain] = useState("");
  const [loading, setLoading] = useState(false);
  const [nameservers, setNameservers] = useState([]);
  const [submitted, setSubmitted] = useState(false);

  const formText = "Search for a domains";

  const handleChange = (event: any) => {
    setDomain(event.target.value);
  };

  const handleSubmit = (event: any) => {
    event.preventDefault();
    setLoading(true);
    setSubmitted(true);
    console.log(domain);

    fetch(`https://dns.google.com/resolve?name=${domain}&type=NS`)
      .then(results => results.json())
      .then(data => {
        setLoading(false);
        if (data && data.Answer) {
          data.Answer.sort((a: any, b: any) => a.data.localeCompare(b.data));
          setNameservers(data.Answer);
        }
      });
  };

  return (
    <>
      <Form onSubmit={handleSubmit}>
        <Form.Row>
          <Col />
          <Col>
            <Form.Control
              type="text"
              placeholder={formText}
              value={domain}
              onChange={handleChange}
              autoFocus
              required
            />
          </Col>
          <Col>
            <Button variant="primary" type="submit">
              <FaSearch /> Search
            </Button>
          </Col>
        </Form.Row>
      </Form>
      <hr />
      {submitted &&
        nameservers.map((server: any, index: number) => (
          <li key={index}>{server.data}</li>
        ))}

      {submitted && nameservers.length === 0 && <small>Error</small>}

      {loading && (
        <Container className="text-center">
          <Spinner animation="grow" size="sm" />
          <Spinner animation="grow" size="sm" />
          <Spinner animation="grow" size="sm" />
        </Container>
      )}
    </>
  );
};

export default SearchForm;

There are multiple ways to share data between components. You can use one of the following options:

  • If the component to which you want to pass the data is a child of SearchForm component, then you can pass it as a prop .

  • If you are managing state via redux, you can connect components to the redux store and get the required data from the redux store in your components.

  • You can also use React's Context API to share common data between components that may or may not have a parent-child relationship.

  • If the component that needs the data from SearchForm component, is a parent component of SearchForm component, you can pass a callback function to the SearchForm component as a prop and when data is available in SearchForm component, call the callback function, received as a prop , and pass the data as an argument to that callback function.

I'd do what Yousaf suggested.

SearchForm should have two props: nameservers handleSubmit

(This will make it easy to write a Storybook story and any tests).

Then make a wrapper for SearchForm that does the API call (handleSubmit) and sets nameservers in context. Now nameservers can be accessed elsewhere in the app.

Ciao, when I want to share data between components I use React-Redux . Lets make an example: Suppose that you want to share data received by server (nameservers). At first install react-redux:

npm install react-redux
npm install redux
npm install @reduxjs/toolkit

Now we have to create the reducer and the action : Lets say you have your component in a folder called "/components/MyComponent". Create a file called MyReducer.js.

/components/MyComponent/MyReducer.js

import { createReducer } from '@reduxjs/toolkit';

const initialState = {
  nameservers: undefined,
};

const LoginReducer = createReducer(initialState, {
   ["SET_NAMESERVERS"]: (state, action) => {
       state.nameservers= action.payload.nameservers;
   },
})

export default MyReducer;

Now, on the same folder, createa file called MyAction.js

/components/MyComponent/MyAction.js

export const setNameServers = data => ({
   type: "SET_NAMESERVERS",
   payload: { nameservers: data }
});

Then create the store : On your project root create a folder callled redux . Inside this create a folder called store . Then on this folder create a file called index.js.

redux/store/index.js

import { createStore, combineReducers } from "redux";
import MyReducer from '../../components/MyComponent/MyReducer';

const reducers = combineReducers({
  MyReducer,
});

const store = createStore(reducers);

export default store;

Now on index.js file on root folder lets pass the store already created:

index.js

...
import { Provider } from 'react-redux';
import store from "./redux/store";

ReactDOM.render((
 <Provider store={store}>
    <App />
 </Provider>
), document.getElementById('root') || document.createElement('div'));

We have almost done. On your component (MyComponent) you retrieve data from server. Once you have data, lets dispatch data to share into the store :

/components/MyComponent/MyComponent.js

...
import { useDispatch } from 'react-redux';
import { setNameServers } from './MyComponentAction';

const MyComponent: React.FC = () => {
   const [nameservers, setNameservers] = useState([]);
   const dispatch = useDispatch();
   ....
   const handleSubmit = (event: any) => {
     ...

fetch(`https://dns.google.com/resolve?name=${domain}&type=NS`)
.then(results => results.json())
.then(data => { 
  setLoading(false);
  if (data && data.Answer) {
    data.Answer.sort((a: any, b: any) => a.data.localeCompare(b.data));
    setNameservers(data.Answer);
    dispatch(setNameServers(data.Answer)); // here the magic
  } 
  });
 };
   
};

Done: now you have nameservers on your react redux store and you can easly get it from another component like this:

OtherComponent.js

import { useSelector } from 'react-redux';
const OtherComponent: React.FC = () => {
   const nameservers = useSelector(state => state.MyReducer.nameservers);
};

And if you log nameservers somewhere in OtherComponent you will see data retrieved in MyComponent . Awesome!

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