Error: Objects are not valid as a React child (but I’m not rendering any object)

I’m trying to retrieve a list of prisons which has a nested object address. It is structured like this:

  { uuid,
    address: {
      city,
      country,
      state,
      street,
      zip },
    prisonName,
    rules,
}

For the sake of simplicity all other objects are strings for now.

I’m getting the error “Error: Objects are not valid as a React child (found: object with keys {city, zip, state, country, street}). If you meant to render a collection of children, use an array instead.” but I’ve poured over my code a dozen times, I know I can’t render Address, it’s an object, not a string, but I don’t render it. I render properties of it, but never the whole object.

I even commented out any instances where I was console.logging the address just to be on the safe side.

prisons.js

import * as ActionTypes from './types'
import PrisonDataService  from '../services/prison.service'

export const createPrison = (prisonName, address, rules) => async (dispatch) => {
    try {
        const res = await PrisonDataService.create({ prisonName, address, rules })
        dispatch({
            type: ActionTypes.CREATE_PRISON,
            payload: res.data,
        });
        return Promise.resolve(res.data);
    } catch (err) {
        return Promise.reject(err)
    }
};

export const retrievePrisons = () => async (dispatch) => {
    try {
        const res = await PrisonDataService.getAll();
        dispatch({ //This is where the compiler says the error is
            type: ActionTypes.RETRIEVE_PRISONS,
            payload: res.data,
        });
    } catch (err) {
        console.log(err)
    }
};

export const updatePrison = (id, data) => async (dispatch) => {
    try {
      const res = await PrisonDataService.update(id, data);
  
      dispatch({
        type: ActionTypes.UPDATE_PRISON,
        payload: data,
      });
  
      return Promise.resolve(res.data);
    } catch (err) {
      return Promise.reject(err);
    }
  };
  
  export const deletePrison = (id) => async (dispatch) => {
    try {
      await PrisonDataService.delete(id);
  
      dispatch({
        type: ActionTypes.DELETE_PRISON,
        payload: { id },
      });
    } catch (err) {
      console.log(err);
    }
  };
  
  export const deleteAllPrisons = () => async (dispatch) => {
    try {
      const res = await PrisonDataService.deleteAll();
  
      dispatch({
        type: ActionTypes.DELETE_ALL_PRISONS,
        payload: res.data,
      });
  
      return Promise.resolve(res.data);
    } catch (err) {
      return Promise.reject(err);
    }
  };
  
  export const findPrisonByName = (name) => async (dispatch) => {
    try {
      const res = await PrisonDataService.findByName(name);
  
      dispatch({
        type: ActionTypes.RETRIEVE_PRISONS,
        payload: res.data,
      });
    } catch (err) {
      console.log(err);
    }
  };

prison-list.component.js

import React, { Component } from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { Button, Col, Input, InputGroup, Label, List, ListGroup } from "reactstrap";
import { retrievePrisons, findPrisonByName, deleteAllPrisons } from "../../actions/prisons";
import AddPrison from "./add-prison.component"

class PrisonsList extends Component {
  constructor(props) {
    super(props);
    this.onChangeSearchName = this.onChangeSearchName.bind(this);
    this.refreshData = this.refreshData.bind(this);
    this.setActivePrison = this.setActivePrison.bind(this);
    this.findByName = this.findByName.bind(this);
    this.removeAllPrisons = this.removeAllPrisons.bind(this);

    this.state = {
      currentPrison: null,
      currentIndex: -1,
      searchName: " ",
    };
  }

  componentDidMount() {
    this.props.retrievePrisons();
  }

  onChangeSearchName(e) {
    const searchName = e.target.value;

    this.setState({
      searchName: searchName,
    });
  }

  refreshData() {
    this.setState({
      currentPrison: null,
      currentIndex: -1,
    });
  }

  setActivePrison(prison, index) {
    console.log(prison)
    this.setState({
      currentPrison: prison,
      currentIndex: index,
    });
  }

  removeAllPrisons() {
    this.props
      .deleteAllPrisons()
      .then((response) => {
        console.log(response);
        this.refreshData();
      })
      .catch((e) => {
        console.log(e);
      });
  }

  findByName() {
    this.refreshData();
    this.props.findPrisonByName(this.state.searchName);
  }

  render() {
    const { searchName, currentPrison, currentIndex } = this.state;
    const { prisons } = this.props;

    console.log(prisons)

    return (
        <List className="row">
        <Col md="8">
          <InputGroup className="mb-3">
            <Input
              type="text"
              className="form-control"
              placeholder="Search by name"
              value={searchName}
              onChange={this.onChangeSearchName}
            />
            <div className="input-group-append">
              <Button
                outline
                color="secondary"
                onClick={this.findByName}
              >
                Search
              </Button>
            </div>
          </InputGroup>
        </Col>
        <div className="col-md-6">
          <h4>Prison List</h4>

          <ListGroup>
            {prisons &&
              prisons.map((prison, index) => (
                <li
                  className={
                    "list-group-item " +
                    (index === currentIndex ? "active" : "")
                  }
                  onClick={() => this.setActivePrison(prison, index)}
                  key={index}
                >
                  {prison.prisonName}
                </li>
              ))}
          </ListGroup>

          <Button
            color="danger"
            className="m-3"
            onClick={this.removeAllPrisons}
          >
            Remove All
          </Button>
        </div>
        <Col md={6}>
          {currentPrison ? (
            <div>
              <h4>Prison</h4>
              <div>
                <Label>
                  <strong>Prison Name:</strong>
                </Label>{" "}
                {currentPrison.prisonName}
              </div>
              <div>
                <Label>
                  <strong>Address:</strong>
                </Label>{" "}
                {currentPrison.address.street}
              </div>
              {/* {console.log(currentPrison.address)} */}
              <div>
                <Label>
                  <strong>UUID:</strong>
                </Label>{" "}
                {currentPrison.uuid}
              </div>
              <div>
                <Label>
                  <strong>Inmates:</strong>
                </Label>{" "}
                {currentPrison.inmates}
              </div>
                <Link
                to={"/prison/" + currentPrison.uuid}
              >
                Edit
              </Link>
            </div>
          ) : (
            <div>
              <br />
              <p>Please click on a Prison...</p>
            </div>
          )}
        </Col>
        <h1>Add Prison</h1>
        <AddPrison/>
      </List>    );
  }
}

const mapStateToProps = (state) => {
  return {
    prisons: state.prisons,
  };
};

export default connect(mapStateToProps, { retrievePrisons, findPrisonByName, deleteAllPrisons })(PrisonsList);

prison.component.js

import React, { Component } from "react";
import { connect } from "react-redux";
import { updatePrison, deletePrison } from "../../actions/prisons";
import PrisonDataService from "../../services/prison.service";
import { Button, Form, FormGroup, Input, Label } from "reactstrap"

class Prison extends Component {
  constructor(props) {
    super(props);
    this.onChangePrisonName = this.onChangePrisonName.bind(this);
    this.onChangeCity = this.onChangeCity.bind(this);
    this.onChangeCountry = this.onChangeCountry.bind(this);
    this.onChangeState = this.onChangeState.bind(this);
    this.onChangeStreet = this.onChangeStreet.bind(this);
    this.onChangeZip = this.onChangeZip.bind(this);
    this.onChangeRules = this.onChangeRules.bind(this);
    this.getPrison = this.getPrison.bind(this);
    this.updateStatus = this.updateStatus.bind(this);
    this.updateContent = this.updateContent.bind(this);
    this.removePrison = this.removePrison.bind(this);

    this.state = {
      currentPrison: {
        uuid: null,
        prisonName: " ",
        address: { 
            city: " ",
            country: " ",
            state: " ",
            street: " ",
            zip: " "
        },
        rules: " "
      },
      message: " ",
    };
  }

  componentDidMount() {
    console.log(this.props)
    this.getPrison(this.props.match && this.props.match.params.uuid);
  }

  onChangePrisonName(e) {
    const prisonName = e.target.value;

    this.setState(function (prevState) {
      return {
        currentPrison: {
          ...prevState.currentPrison,
          prisonName: prisonName,
        },
      };
    });
  }

  onChangeCity(e) {
    var newAddress = {...this.state.currentPrison.address}
    address.city = e.target.value;

    this.setState(function (prevState) {
      return {
        currentPrison: {
          ...prevState.currentPrison,
          address: newAddress,
        },
      };
    });
  }

  onChangeCountry(e) {
    var newAddress = {...this.state.currentPrison.address}
    address.country = e.target.value;

    this.setState(function (prevState) {
      return {
        currentPrison: {
          ...prevState.currentPrison,
          address: newAddress,
        },
      };
    });
  }

  onChangeState(e) {
    var newAddress = {...this.state.currentPrison.address}
    address.state = e.target.value;

    this.setState(function (prevState) {
      return {
        currentPrison: {
          ...prevState.currentPrison,
          address: newAddress,
        },
      };
    });
  }

  onChangeStreet(e) {
    var newAddress = {...this.state.currentPrison.address}
    address.street = e.target.value;

    this.setState(function (prevState) {
      return {
        currentPrison: {
          ...prevState.currentPrison,
          address: newAddress,
        },
      };
    });
  }

  onChangeZip(e) {
    var newAddress = {...this.state.currentPrison.address}
    address.zip = e.target.value;

    this.setState(function (prevState) {
      return {
        currentPrison: {
          ...prevState.currentPrison,
          address: newAddress,
        },
      };
    });
  }

  onChangeRules(e) {
    const rules = e.target.value;

    this.setState((prevState) => ({
      currentPrison: {
        ...prevState.currentPrison,
        rules: rules,
      },
    }));
  }

  getPrison(uuid) {
    PrisonDataService.get(uuid)
      .then((response) => {
        this.setState({
          currentPrison: response.data,
        });
        console.log(response.data);
      })
      .catch((e) => {
        console.log(e);
      });
  }

  updateStatus(status) {
    var address = {
      street: this.state.currentPrison.address.street,
      state: this.state.currentPrison.address.state,
      country: this.state.currentPrison.address.country,
      city: this.state.currentPrison.address.city,
      zip: this.state.currentPrison.address.zip
    }
    var data = {
      uuid: this.state.currentPrison.uuid,
      prisonName: this.state.currentPrison.prisonName,
      address: address,
      rules: this.state.currentPrison.rules,
    };

    this.props
      .updatePrison(this.state.currentPrison.uuid, data)
      .then((response) => {
        this.setState((prevState) => ({
          currentPrison: {
            ...prevState.currentPrison,
          },
        }));

        this.setState({ message: "The status was updated successfully!" });
      })
      .catch((e) => {
        console.log(e);
      });
  }

  updateContent() {
    this.props
      .updatePrison(this.state.currentPrison.uuid, this.state.currentPrison)
      .then((response) => {
        console.log(response);
        this.setState({ message: "The prison was updated successfully!" });
      })
      .catch((e) => {
        console.log(e);
      });
  }

  removePrison() {
    this.props
      .deletePrison(this.state.currentPrison.uuid)
      .then(() => {
        this.props.history.push("/prisons");
      })
      .catch((e) => {
        console.log(e);
      });
  }

  render() {
    const { currentPrison } = this.state;

    return (
      <div>
      {currentPrison ? (
        <div className="edit-form">
          <h4>Prison</h4>
          <Form>
            <FormGroup>
              <Label htmlFor="prisonName">Prison Name</Label>
              <Input
                type="text"
                className="form-control"
                id="prisonName"
                value={currentPrison.prisonName}
                onChange={this.onChangePrisonName}
              />
            </FormGroup>
            {/* {console.log(currentPrison.address)} */}
            {/* <FormGroup>
              <Label htmlFor="city">City</Label>
              <Input
                type="text"
                className="form-control"
                id="city"
                value={currentPrison.address.city}
                onChange={this.onChangeCity}
              />
            </FormGroup>
            <FormGroup>
              <Label htmlFor="country">Country</Label>
              <Input
                type="text"
                className="form-control"
                id="country"
                value={currentPrison.address.country}
                onChange={this.onChangeCountry}
              />
            </FormGroup>
            <FormGroup>
              <Label htmlFor="state">State</Label>
              <Input
                type="text"
                className="form-control"
                id="state"
                value={currentPrison.address.state}
                onChange={this.onChangeState}
              />
            </FormGroup>
            <FormGroup>
              <Label htmlFor="street">Street</Label>
              <Input
                type="text"
                className="form-control"
                id="street"
                value={currentPrison.address.street}
                onChange={this.onChangeStreet}
              />
            </FormGroup>
            <FormGroup>
              <Label htmlFor="zip">Zip</Label>
              <Input
                type="text"
                className="form-control"
                id="zip"
                value={currentPrison.address.zip}
                onChange={this.onChangeZip}
              />
            </FormGroup> */}
            <FormGroup>
              <Label for="rules">Rules</Label>
              <Input
                type="text"
                className="form-control"
                id="rules"
                value={currentPrison.rules}
                onChange={this.onChangeRules}
              />
            </FormGroup>
          </Form>

          <Button
          color="danger"
          onClick={this.removePrison}
          >
            Delete
          </Button>

          <Button
            type="submit"
            color="primary"
            onClick={this.updateContent}
          >
            Update
          </Button>
          <p>{this.state.message}</p>
        </div>
      ) : (
        <div>
          <br />
          <p>Please click on a Prison...</p>
        </div>
      )}
    </div>
    );
  }
}

export default connect(null, { updatePrison, deletePrison })(Prison);

add-prison.component.js

import React, { Component } from "react";
import { connect } from "react-redux";
import { Button, FormGroup, Label, Input } from "reactstrap";
import { createPrison } from "../../actions/prisons";

class AddPrison extends Component {
  constructor(props) {
    super(props);
    this.onChangeCity = this.onChangeCity.bind(this);
    this.onChangeCountry = this.onChangeCountry.bind(this);
    this.onChangeState = this.onChangeState.bind(this);
    this.onChangeStreet = this.onChangeStreet.bind(this);
    this.onChangeZip = this.onChangeZip.bind(this);
    this.onChangePrisonName = this.onChangePrisonName.bind(this);
    this.onChangeRules = this.onChangeRules.bind(this);
    this.savePrison = this.savePrison.bind(this);
    this.newPrison = this.newPrison.bind(this);

    this.state = {
      uuid: null,
      address: {
        city: " ",
        country: " ",
        state: " ",
        street: " ",
        zip: " ",
      },
      prisonName: " ",
      rules: " ",
    };
  }

  onChangeCity(e) {
    var newAddress = { ...this.state.address }
    newAddress.city = e.target.value
    this.setState({
      address: newAddress,
    });
  }
    
  onChangeCountry(e) {
    var newAddress = { ...this.state.address }
    newAddress.country = e.target.value
    this.setState({
      address: newAddress,
    });
  }

  onChangeState(e) {
    var newAddress = { ...this.state.address }
    newAddress.state = e.target.value
    this.setState({
      address: newAddress,
    });
  }

  onChangeStreet(e) {
    var newAddress = { ...this.state.address }
    newAddress.street = e.target.value
    this.setState({
      address: newAddress,
    });
  }

  onChangeZip(e) {
    var newAddress = { ...this.state.address }
    newAddress.zip = e.target.value
    this.setState({
      address: newAddress,
    });
  }

  onChangeRules(e) {
    console.log(e.target.value)
    this.setState({
      rules: e.target.value,
    })
  }

  onChangePrisonName(e) {
    console.log(e.target.value)
    this.setState({
      prisonName: e.target.value
    })
  }

  savePrison() {
    const { prisonName, address, rules } = this.state;
    console.log(address)
    this.props
      .createPrison(prisonName, address, rules)
      .then((data) => {
        this.setState({
          prisonName: data.prisonName,
          address: data.address,
          rules: data.rules,
        });
        console.log(data);
      })
      .catch((e) => {
        console.log(e);
      });
  }

  newPrison() {
    this.setState ({
      uuid: null,
      address: {
        city: " ",
        country: " ",
        state: " ",
        street: " ",
        zip: " ",
      },
      prisonName: " ",
      rules: "",
    });
  }

  render() {
    return (
      <div className="submit-form">
        {this.state.submitted ? (
          <div>
            <h4>You submitted successfully!</h4>
            <Button color="success" onClick={this.newPrison}>
              Add
            </Button>
          </div>
        ) : (
          <div>
            <FormGroup>
              <Label htmlFor="prisonName">Prison Name</Label>
              <Input
                type="text"
                className="form-control"
                id="prisonName"
                required
                value={this.state.prisonName}
                onChange={this.onChangePrisonName}
                name="prisonName"
              />
            </FormGroup>
            <FormGroup>
              <Label htmlFor="city">City</Label>
              <Input
                type="text"
                className="form-control"
                id="city"
                required
                value={this.state.address.city}
                onChange={this.onChangeCity}
                name="city"
              />
            </FormGroup>
            <FormGroup>
              <Label htmlFor="country">Country</Label>
              <Input
                type="text"
                className="form-control"
                id="country"
                required
                value={this.state.address.country}
                onChange={this.onChangeCountry}
                name="prison"
              />
            </FormGroup>
            <FormGroup>
              <Label htmlFor="state">State</Label>
              <Input
                type="text"
                className="form-control"
                id="state"
                required
                value={this.state.address.state}
                onChange={this.onChangeState}
                name="state"
              />
            </FormGroup>
            <FormGroup>
              <Label htmlFor="street">Street</Label>
              <Input
                type="text"
                className="form-control"
                id="street"
                required
                value={this.state.address.street}
                onChange={this.onChangeStreet}
                name="releaseDate"
              />
            </FormGroup>
            <FormGroup>
              <Label htmlFor="zip">Zip</Label>
              <Input
                type="text"
                className="form-control"
                id="zip"
                required
                value={this.state.address.zip}
                onChange={this.onChangeZip}
                name="releaseDate"
              />
            </FormGroup>
            <FormGroup>
              <Label htmlFor="rules">Rules</Label>
              <Input
                type="text"
                className="form-control"
                id="rules"
                required
                value={this.state.rules}
                onChange={this.onChangeRules}
                name="rules"
              />
            </FormGroup>

            <Button onClick={this.savePrison} color="success">
              Submit
            </Button>
          </div>
        )}
      </div>
    );
  }
}

export default connect(null, { createPrison })(AddPrison);