简体   繁体   中英

setting state from a different component

First of all, my approach could just be misguided from the start.

I have a component that lists objects added by a sibling component.

I would like the list component to update when a new object is added.

As you can see, I'm calling the same function (getHostedServiceList) in both components. Obviously, this would need t be cleaned up, but I'd like to get it working first

I'm using hooks to accomplish this.

//input

const options = [
  { value: '1', label: '1' },
  { value: '2', label: '2' },
  { value: '3', label: '3' },
];

// class Remotes extends Component {
const Remotes = ({ ...props }) => {
  const [service, setService] = useState();
  const [url, setUrl] = useState();
  const [token, setToken] = useState();
  const [displayName, setDisplayName] = useState();
  const [apiUrl, setApiUrl] = useState();
  const [services, setServices] = useState();

  let HOME = process.env.HOME || '';
  if (process.platform === 'win32') {
    HOME = process.env.USERPROFILE || '';
  }

  const getHostedServiceList = () => {
    console.log('props', props);
    if (!fs.existsSync(`${HOME}/providers.json`)) {
      return newMessage(
        `Unable to locate ${HOME}/providers.json`,
        'error',
      );
    }
    const payload = JSON.parse(
      fs.readFileSync(`${HOME}/providers.json`),
    );

    setServices(payload);
  };

  const setProvider = selectedOption => {
    setService(selectedOption.value);
    setUrl(`http://www.${selectedOption.value}.com`);
    setApiUrl(`http://www.${selectedOption.value}.com/api/v1`);
  };

  const { onAddRemote } = props;
  return (
    <div>
      <div>Add a remote host:</div>
      <StyledSelect
        value="Select Provider"
        onChange={setProvider}
        options={options}
      />
      {console.log('service', service)}
      <TextInput
        label="Url"
        defaultValue={url}
        onChange={e => {
          setProvider(e.target.value);
        }}
        disabled={!service ? 'disabled' : ''}
      />

      <TextInput
        label="API Url"
        defaultValue={apiUrl}
        onChange={e => setApiUrl(e.target.value)}
        disabled={!service ? 'disabled' : ''}
      />

      <TextInput
        label="Token"
        onChange={e => setToken(e.target.value)}
        disabled={!service ? 'disabled' : ''}
      />

      <TextInput
        label="Display Name"
        onChange={e => setDisplayName(e.target.value)}
        disabled={!service ? 'disabled' : ''}
      />

      <Button
        disabled={!service || !url || !token}
        onClick={() => {
          onAddRemote({ service, url, apiUrl, token, displayName });
          getHostedServiceList();
        }}
      >
        Add Remote
      </Button>
    </div>
  );
};

//list


const HostedProviderList = ({ ...props }) => {
  const [services, setServices] = useState();

  let HOME = process.env.HOME || '';
  if (process.platform === 'win32') {
    HOME = process.env.USERPROFILE || '';
  }

  const getHostedServiceList = () => {
    console.log('props', props);
    if (!fs.existsSync(`${HOME}/providers.json`)) {
      return newMessage(
        `Unable to locate ${HOME}/providers.json`,
        'error',
      );
    }
    const payload = JSON.parse(
      fs.readFileSync(`${HOME}/providers.json`),
    );

    setServices(payload);
  };

  useEffect(() => {
    // console.log('props 1', services);
    getHostedServiceList();
  }, []);

  return (
    <Wrapper>
      <Flexbox>
        <Title>Provider List</Title>
      </Flexbox>
      <div>
        {services &&
          services.map((service, i) => (
            <Service key={i}>
              <ServiceName>{service.displayName}</ServiceName>
              <ServiceProvider>{service.service}</ServiceProvider>
            </Service>
          ))}
      </div>
    </Wrapper>
  );
};

I would like the list component to update when a new object is added.

This is where something like Redux or MobX comes in handy. These tools allow you to create a "store" - the place where you load and store data used by different components throughout your app. You then connect your store to individual components which interact with the data (displaying a list, displaying a create/edit form, etc). Whenever one component modifies the data, all other components will receive the updates automatically.

One way this cross-communication is accomplished is through apub/sub mechanism - whenever one component creates or modifies data, it publishes (or "dispatches") an event. Other components subscribe (or "listen") for these events and react (or "re-render") accordingly. I will leave the research and implementation up to the reader as it cannot be quickly summarized in a StackOverflow answer.

You might also try the new React hooks , as this allows you to easily share data between components. If you choose this option, please take special care to do it properly as it is easy to be lazy and irresponsible.

To get you started, here are some articles to read. I highly recommend reading the first one:

Yes, you could use Redux (or React's own 'context') for global state handling. However, a simpler solution to be considered might just be to send the data to the parent and pass to the list component like so:

class Parent extends Component {
  state = { objectsAdded: [] }

  onObjectAdded = ( obj ) => {
    // deepclone may be needed
    this.setState({objectsAdded: this.state.objectsAdded.concat(obj)})
  }

  render() {
    return (
      <Fragment>
        <ListComponent objects={this.state.objectsAdded} />
        <ObjectAdder onObjectAdded={this.onObjectAdded} />
      </Fragment>
  }
}

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