htmlreactjsrenderdescendant

HTML JavaScript React: Front-End React Component Throwing react-dom.development.js:506 Warning


I have rendered a front-end react / redux component using JSX syntax. It executes but throws the following warning in my console:

enter image description here

Below is my code:

import React from 'react'
import {connect} from 'react-redux'
import {getUsers, deleteUserThunk} from '../store/allUsers'
import {updateUserThunk, fetchSingleUser} from '../store/singleUser'
// Status Filter import BeerFilter from './BeerFilter'
import Card from 'react-bootstrap/Card'
import Button from 'react-bootstrap/Button'
import {UncontrolledCollapse} from 'reactstrap'

export class AllUsers extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      showForm: false,
      stat: ''
    }
    this.handleChange = this.handleChange.bind(this)
    this.handleSubmit = this.handleSubmit.bind(this)
  }

  componentDidMount() {
    try {
      this.props.fetchInitialUsers()
      this.props.deleteUserThunk()
    } catch (error) {
      console.error(error)
    }
  }

  clickHandlerOne() {
    let hidden = this.state.showForm
    this.setState({
      showForm: !hidden
    })
  }

  handleChange(event) {
    //console.log('event.target', event.target)
    this.setState({
      [event.target.name]: event.target.value
    })
  }

  async handleSubmit(userId) {
    event.preventDefault()

    const updatedUser = {
      id: userId,
      isAdmin: this.state.stat
    }

    // console.log('UPDATE USER', updatedUser)

    await this.props.updateUserThunk(updatedUser)
    this.props.fetchInitialUsers()
  }

  render() {
    const users = this.props.users
    // console.log('PROPS', this.props)
    console.log('USERS', this.props.users)

    return (
      <div>
        {/* <div className="options">
          <select onChange={this.handleChange}>
            <option value="">Sort By...</option>
            <option value="priceHighToLow">Price (high to low)</option>
            <option value="priceLowToHigh">Price (low to high)</option>
            <option value="name">Name</option>
          </select>

          <BeerFilter />
          </div> */}

        <div className="flex-cards">
          {users.map(user => (
            <Card style={{width: '18rem'}} key={user.id}>
              {/* delete thunk */}
              <span>
                <p>
                  <Button
                    id={`delete${user.id}`}
                    variant="danger"
                    onClick={() => this.props.deleteUserThunk(user.id)}
                  >
                    X
                  </Button>
                </p>
              </span>

              <Card.Body>
                <Card.Title>User Id: {user.id}</Card.Title>
                <Card.Text>
                  <div>
                    <ul>
                      <li>
                        <div className="highlight">
                          <img src={user.imageUrl} />
                        </div>
                        <div className="details">
                          <p>Username: {user.username}</p>
                          <p>User Email: {user.email}</p>
                          <p>Admin Status: {user.isAdmin ? 'true' : 'false'}</p>
                          <p>
                            Created Date:{' '}
                            {new Intl.DateTimeFormat('en-GB', {
                              month: 'short',
                              day: '2-digit',
                              year: 'numeric'
                            }).format(new Date(user.createdAt))}
                          </p>
                          <p />
                          <Button
                            id={`user${user.id}`}
                            onClick={() => {
                              this.clickHandlerOne()
                            }}
                            variant="outline-info"
                          >
                            Admin Status Toggle
                          </Button>
                          <UncontrolledCollapse toggler={`#user${user.id}`}>
                            {/* {this.state.showForm && (
                              <UpdateUserStatus userId={user.id} />
                            )} */}
                            <form onSubmit={() => this.handleSubmit(user.id)}>
                              <div>
                                <span>
                                  <select
                                    name="stat"
                                    value={
                                      typeof user.isAdmin === 'string'
                                        ? this.state.isAdmin
                                        : user.isAdmin
                                    }
                                    onChange={this.handleChange}
                                  >
                                    <option value="true">true</option>
                                    <option value="false">false</option>
                                  </select>
                                </span>

                                <p />
                                <span>
                                  <p>
                                    {/* */}
                                    <button type="submit">Submit</button>
                                  </p>
                                </span>
                              </div>
                            </form>
                          </UncontrolledCollapse>
                        </div>
                      </li>
                    </ul>
                  </div>
                </Card.Text>
              </Card.Body>
            </Card>
          ))}
        </div>
      </div>
    )
  }
}

const mapStateToProps = state => {
  return {
    users: state.allUsers
  }
}

const mapDispatchToProps = dispatch => {
  return {
    loadSingleUser: id => dispatch(fetchSingleUser(id)),
    updateUserThunk: updatedUser => dispatch(updateUserThunk(updatedUser)),
    //getSortedBeers: (sortBy, beers) => dispatch(sortBeers(sortBy, beers)),
    fetchInitialUsers: () => dispatch(getUsers()),
    deleteUserThunk: userId => dispatch(deleteUserThunk(userId))
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(AllUsers)

In my web page I (not always but usually) have to click the edit button twice for my order status to update and render on my page. I'm wondering if the warning has anything to do with this.

enter image description here

What am I doing wrong? I am very new to this type of coding so specificity in responses would be greatly appreciated.


Solution

  • There are a few changes that I would make in addition to addressing your console error.

    1. Bind this to clickHandlerOne
    2. Don't use <p> tags for the sake of adopting their styling. Example:

      This makes sense to me. It's a paragraph of text.

      <p>Username: {user.username}</p>
      

      This doesn't make sense to me. I think you're wanting a <button> with certain spacing styles:

      <span>
        <p>
          <Button
            id={`delete${user.id}`}
            variant="danger"
            onClick={() => this.props.deleteUserThunk(user.id)}
          >
            X
          </Button>
        </p>
      </span>
      

    Try out this code and see how it works:

    import React from "react";
    import { connect } from "react-redux";
    import { getUsers, deleteUserThunk } from "../store/allUsers";
    import { updateUserThunk, fetchSingleUser } from "../store/singleUser";
    // Status Filter import BeerFilter from './BeerFilter'
    import Card from "react-bootstrap/Card";
    import Button from "react-bootstrap/Button";
    import { UncontrolledCollapse } from "reactstrap";
    
    export class AllUsers extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          showForm: false,
          stat: ""
        };
        this.clickHandlerOne = this.clickHandlerOne.bind(this);
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
      }
    
      componentDidMount() {
        try {
          this.props.fetchInitialUsers();
          this.props.deleteUserThunk();
        } catch (error) {
          console.error(error);
        }
      }
    
      clickHandlerOne() {
        const hidden = this.state.showForm;
        this.setState({
          showForm: !hidden
        });
      }
    
      handleChange(event) {
        //console.log('event.target', event.target)
        this.setState({
          [event.target.name]: event.target.value
        });
      }
    
      async handleSubmit(userId) {
        // event.preventDefault(); // I don't think you ave an `event` in scope here
    
        const updatedUser = {
          id: userId,
          isAdmin: this.state.stat
        };
    
        // console.log('UPDATE USER', updatedUser)
    
        await this.props.updateUserThunk(updatedUser);
        this.props.fetchInitialUsers();
      }
    
      render() {
        const users = this.props.users;
        // console.log('PROPS', this.props)
        console.log("USERS", this.props.users);
    
        return (
          <div>
            {/* <div className="options">
              <select onChange={this.handleChange}>
                <option value="">Sort By...</option>
                <option value="priceHighToLow">Price (high to low)</option>
                <option value="priceLowToHigh">Price (low to high)</option>
                <option value="name">Name</option>
              </select>
    
              <BeerFilter />
              </div> */}
    
            <div className="flex-cards">
              {users.map(user => (
                <Card style={{ width: "18rem" }} key={user.id}>
                  {/* delete thunk */}
                    <div>
                      <Button
                        id={`delete${user.id}`}
                        variant="danger"
                        onClick={() => this.props.deleteUserThunk(user.id)}
                      >
                        X
                      </Button>
                    </div>
    
                  <Card.Body>
                    <Card.Title>User Id: {user.id}</Card.Title>
                    <Card.Text>
                      <div>
                        <ul>
                          <li>
                            <div className="highlight">
                              <img src={user.imageUrl} />
                            </div>
                            <div className="details">
                              <p>Username: {user.username}</p>
                              <p>User Email: {user.email}</p>
                              <p>Admin Status: {user.isAdmin ? "true" : "false"}</p>
                              <p>
                                Created Date:{" "}
                                {new Intl.DateTimeFormat("en-GB", {
                                  month: "short",
                                  day: "2-digit",
                                  year: "numeric"
                                }).format(new Date(user.createdAt))}
                              </p>
                              <Button
                                id={`user${user.id}`}
                                onClick={() => {
                                  this.clickHandlerOne();
                                }}
                                variant="outline-info"
                              >
                                Admin Status Toggle
                              </Button>
                              <UncontrolledCollapse toggler={`#user${user.id}`}>
                                {/* {this.state.showForm && (
                                  <UpdateUserStatus userId={user.id} />
                                )} */}
                                <form onSubmit={() => this.handleSubmit(user.id)}>
                                  <div>
                                      <select
                                        name="stat"
                                        value={
                                          typeof user.isAdmin === "string"
                                            ? this.state.isAdmin
                                            : user.isAdmin
                                        }
                                        onChange={this.handleChange}
                                      >
                                        <option value="true">true</option>
                                        <option value="false">false</option>
                                      </select>
    
                                      <div>
                                        {/* */}
                                        <button type="submit">Submit</button>
                                      </div>
                                  </div>
                                </form>
                              </UncontrolledCollapse>
                            </div>
                          </li>
                        </ul>
                      </div>
                    </Card.Text>
                  </Card.Body>
                </Card>
              ))}
            </div>
          </div>
        );
      }
    }
    
    const mapStateToProps = state => {
      return {
        users: state.allUsers
      };
    };
    
    const mapDispatchToProps = dispatch => {
      return {
        loadSingleUser: id => dispatch(fetchSingleUser(id)),
        updateUserThunk: updatedUser => dispatch(updateUserThunk(updatedUser)),
        //getSortedBeers: (sortBy, beers) => dispatch(sortBeers(sortBy, beers)),
        fetchInitialUsers: () => dispatch(getUsers()),
        deleteUserThunk: userId => dispatch(deleteUserThunk(userId))
      };
    };
    
    export default connect(
      mapStateToProps,
      mapDispatchToProps
    )(AllUsers);
    

    To get the styles that you were relying on from the <p>, you can inline styles, or use CSS via the className prop.

    There's also a few CSS-in-JS libraries to be aware of, such as styled-components.

    Let me know if this fixes your issue.