Мой хук useState не работает в определенной части моего кода, но он работает везде. Почему это происходит, несмотря на использование идентичного кода?

#reactjs #use-state

#reactjs #use-state

Вопрос:

Я использовал хук useState() во всем моем проекте для управления формами. Он всегда работал так, как задумано. Однако в компоненте RoleEditCard я вообще не смог использовать хук useState.

Если я сделаю это:

     const [roleName, setRoleName] = useState(props.roleInfo.role_name);
    console.log(roleName, props.roleInfo.role_name);
  

Я получаю следующее в консоли:
undefined New Role
С «Новой ролью», являющейся ожидаемым значением обеих переменных. В чем проблема?

Мой полный код (здесь использовались только reactjs и react-bootstrap):

 // Here, we handle the form for adding new role.

// Package imports
import React, { useState } from 'react';
// import { Multiselect } from 'multiselect-react-dropdown';

// React Bootstrap components import
import Container from 'react-bootstrap/Container';
import Accordion from 'react-bootstrap/Accordion';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import Card from 'react-bootstrap/Card';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import { FaTimes, FaCheck } from 'react-icons/fa';

// CSS Module imports
import classes from './RoleEditCard.module.css';

const RoleEditCard = (props) => {
    // State handling
    const [roleName, setRoleName] = useState(props.roleInfo.role_name);
    const [roleDescription, setRoleDescription] = useState(props.roleInfo.role_description);
    const [rolePrivileges, setRolePrivileges] = useState([...props.privilegeInfo]);
    const [roleInfo, setRoleInfo] = useState({...props.roleInfo});
    console.log(roleInfo, props.roleInfo);

    // Handle submission of form
    const submitHandler = event => {
        if (roleName !== props.role.role_name || roleDescription !== props.role.role_description || JSON.stringify(rolePrivileges) !== JSON.stringify(props.role.role_privileges)) {
            // Send details to container
            props.onSubmission({
                roleName: roleName,
                roleDescription: roleDescription,
                rolePrivileges: rolePrivileges.map(optionObject => optionObject.sub_menu_name)
            });
        } else {
            console.log("NO CHANGES!");
        }
        // Prevent page reload
        event.preventDefault();
    }

    const resetHandler = event => {
        setRoleName("");
        setRoleDescription("");
        setRolePrivileges([]);
        // Prevent page reload
        event.preventDefault();
    }

    return (
        <Container className={classes.PageContents}>
            <Row>
                <Col sm={{ span: '8', offset: '2' }}>
                    <Card className={classes.editCard}>
                        <Card.Body>
                            <Form onSubmit={submitHandler}>
                                <Form.Row className='text-left'>
                                    <Form.Group as={Col} controlId="formRoleName" md='6'>
                                        <Form.Label>Role Name:</Form.Label>
                                        <Form.Control 
                                            placeholder="Role Name" 
                                            value={roleInfo.role_name} 
                                            required
                                            onChange={event => { setRoleInfo({ ...roleInfo, role_name: event.target.value })}} 
                                        />
                                    </Form.Group>
                                </Form.Row>

                                <Form.Row className='text-left'>
                                    <Form.Group as={Col} controlId="formRoleDescription" md='12'>
                                        <Form.Label>Description:</Form.Label>
                                        <Form.Control 
                                            type='textarea' 
                                            placeholder="Describe the Role" 
                                            value={roleDescription} 
                                            required
                                            onChange={event => { setRoleDescription(event.target.value) }}
                                        />
                                    </Form.Group>
                                </Form.Row>

                                {/* Privilege Selection */}
                                <Form.Row className='text-left'>
                                    <Form.Group as={Col} controlId='formPrivilege'>
                                        <Form.Label>Select Privileges</Form.Label>
                                        <Accordion defaultActiveKey="0">
                                            {props.privileges.map((mainmenu, index) => (
                                                <Card key={mainmenu.id}>
                                                    <Card.Header>
                                                        <Row>
                                                            <Accordion.Toggle as={Col} variant="link" eventKey={mainmenu.id}>
                                                                {mainmenu.main_menu_name}
                                                            </Accordion.Toggle>
                                                        </Row>
                                                    </Card.Header>
                                                    { mainmenu.Sub_Menus.length ? (
                                                        <Accordion.Collapse eventKey={mainmenu.id}>
                                                            <Card.Body>
                                                                {mainmenu.Sub_Menus.map(submenu => (
                                                                    <Form.Check
                                                                        type='checkbox'
                                                                        value={submenu.sub_menu_name}
                                                                        key={submenu.sub_menu_name}
                                                                        defaultChecked={props.originalPrivileges.indexOf(submenu.sub_menu_name) !== -1}
                                                                        onClick={event => {
                                                                            const curVal = event.target.value;
                                                                            const privList = JSON.parse(JSON.stringify(rolePrivileges));
                                                                            if (privList.indexOf(curVal) !== -1) {
                                                                                privList.splice(privList.indexOf(curVal), 1);
                                                                            } else {
                                                                                privList.push(curVal);
                                                                            }
                                                                            setRolePrivileges(privList);
                                                                        }}
                                                                        label={submenu.sub_menu_name}
                                                                    />
                                                                ))}
                                                            </Card.Body>
                                                        </Accordion.Collapse>
                                                    ) : null}
                                                </Card>
                                            ))}
                                        </Accordion>
                                    </Form.Group>
                                </Form.Row>

                                <Form.Row className='justify-content-md-center'>
                                    <Col md='auto'>
                                        <Button variant="primary" type="submit" className={classes.FormButton}>
                                            <FaCheck color='white' height='24' /> Submit
                                        </Button>
                                    </Col>
                                    <Col md='auto'></Col>
                                    <Col md='auto'>
                                        <Button variant="danger" type="button" className={classes.FormButton} onClick={resetHandler}>
                                            <FaTimes color='white' height='24' /> Reset
                                        </Button>
                                    </Col>
                                </Form.Row>
                            </Form>
                        </Card.Body>
                    </Card>
                </Col>
            </Row>
        </Container>
    )
}

export default RoleEditCard

  

Вот как я вызвал RoleEditCard:

                     <RoleEditCard
                        onSubmission={this.submitHandler}
                        roleInfo={this.state.roleDetails}
                        privilegeInfo={this.state.privilegeDetails}
                        originalPrivileges={this.state.originalPrivileges}
                        privileges={this.state.records}
                    />
  

Примечание: roleDetails, privilegeDetails и т. Д. были определены в componentDidMount() компонента класса.

Кроме того, я также использовал аналогичный код useState в UserEditCard:

 // Package import
import React, { useState } from 'react';

// Bootstrap Components
import Container from 'react-bootstrap/Container';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import Card from 'react-bootstrap/Card';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import { FaTimes, FaCheck } from 'react-icons/fa';

// CSS Modules
import classes from './UserEditCard.module.css';

const UserEditCard = (props) => {
    const [fullName, setFullName] = useState(props.UserInfo.full_name);
    const [phoneNumber, setPhoneNumber] = useState(props.UserInfo.phone_number);
    const [status, setStatus] = useState(props.UserInfo.status);

    const submitHandler = event => {
        //this.onSubmission
        if (fullName !== props.UserInfo.full_name || phoneNumber !== props.UserInfo.phone_number || status !== props.UserInfo.status) {
            props.onSubmission({
                fullName: fullName,
                phoneNumber: phoneNumber,
                status: status
            });
        } else {
            console.log("NO CHANGE!");
        }
        event.preventDefault();
    }

    const resetHandler = event => {
        setFullName(props.UserInfo.full_name);
        setPhoneNumber(props.UserInfo.phone_number);
        setStatus(props.UserInfo.status);
        // Prevent page reload
        event.preventDefault();
    }

    return (
        <Container className={classes.PageContents}>
            <Row>
                <Col sm={{ span: '8', offset: '2' }}>
                    <Card className={classes.editCard}>
                        <Card.Body>
                            <Form onSubmit={submitHandler}>

                                <Form.Row className='text-left'>
                                    <Form.Group as={Col} controlId="formFullName" md='6'>
                                        <Form.Label>Full Name:</Form.Label>
                                        <Form.Control
                                            value={fullName}
                                            placeholder="Full Name"
                                            required
                                            onChange={event => {
                                                setFullName(event.target.value);
                                            }}
                                        />
                                    </Form.Group>
                                </Form.Row>

                                <Form.Row className='text-left'>
                                    <Form.Group as={Col} controlId="formEmailId" md='6'>
                                        <Form.Label>Email Address:</Form.Label>
                                        <Form.Control
                                            value={props.UserInfo.email_id}
                                            disabled
                                        />
                                    </Form.Group>

                                    <Form.Group as={Col} controlId="formPhoneNumber" md='6'>
                                        <Form.Label>Phone Number:</Form.Label>
                                        <Form.Control
                                            placeholder="1234567890"
                                            value={phoneNumber}
                                            required
                                            // May start with plus; cannot end with whitespace; maximum length of 20 characters
                                            pattern='^(?= ?d ( ?d )*$).{1,20}$'
                                            onChange={event => {
                                                setPhoneNumber(event.target.value);
                                            }}
                                        />
                                    </Form.Group>
                                </Form.Row>

                                <Form.Row className='text-left'>
                                    <Form.Group as={Col} controlId="formRoleSelect">
                                        <Form.Label>Role</Form.Label>
                                        <Form.Control
                                            as="select"
                                            value={props.UserInfo.role_name}
                                            disabled
                                        >
                                            <option value={props.UserInfo.role_name}>{props.UserInfo.role_name}</option>
                                            {/* Company List */}
                                            {/* {props.roleOptions.map(role => <option value={role} key={role}>{role}</option>)} */}
                                        </Form.Control>
                                    </Form.Group>

                                    <Form.Group as={Col}>
                                        <Form.Label>Status</Form.Label>
                                        <Form.Control
                                            as="select"
                                            value={status}
                                            onChange={event => {
                                                setStatus(event.target.value);
                                            }}
                                            required
                                        >
                                            <option value='Active'>Active</option>
                                            <option value='Inactive'>Inactive</option>
                                            <option value='Deleted'>Deleted</option>
                                        </Form.Control>
                                    </Form.Group>
                                </Form.Row>

                                <Form.Row className='justify-content-md-center'>
                                    <Col md='auto'>
                                        <Button variant="primary" type="submit" className={classes.FormButton}>
                                            <FaCheck color='white' height='24' /> Submit
                                        </Button>
                                    </Col>
                                    <Col md='auto'></Col>
                                    <Col md='auto'>
                                        <Button variant="danger" type="button" className={classes.FormButton} onClick={resetHandler}>
                                            <FaTimes color='white' height='24' /> Reset
                                        </Button>
                                    </Col>
                                </Form.Row>
                            </Form>
                        </Card.Body>
                    </Card>
                </Col>
            </Row>
        </Container>
    )
}

export default UserEditCard
  

Комментарии:

1. Попробуйте напечатать это состояние внутри useEffect при визуализации компонента useEffect(() => console.log(roleInfo, props.roleInfo), []) . Вероятно, это не определено, потому что реквизиты еще не были переданы вашему компоненту RoleEditCard

2. На это невозможно ответить, не показав нам, как вы используете <RoleEditCard ... /> .

3. Вы асинхронно извлекаете roleInfo данные из удаленного ресурса? Вы видите другой журнал перед этим журналом с обоими значениями undefined ? Обратите внимание, что начальное значение to useState устанавливается только один раз при монтировании, поэтому, если оно находится undefined на монтировании, но позже изменяется на какое-то значение, вы не увидите, что это отражено в состоянии, пока вы явно не обновите его новым значением.

4. Попробуйте вести журнал следующим образом: console.log('rendering',JSON.stringify(roleInfo), JSON.stringify(props.roleInfo)); сколько раз он регистрируется и что он регистрирует?

5. Примечание: roleDetails, privilegeDetails и т. Д. были определены в componentDidMount() компонента класса . Ваш родительский componentDidMount элемент вызывается только после того, как ваш дочерний элемент монтируется, и в этот момент начальное состояние уже будет установлено. Вот почему вы видите это как undefined . Итак useState , хук работает правильно. Но если ваш родитель действительно управляет всеми этими данными, ваш компонент, вероятно, должен контролироваться и вообще не иметь собственного состояния. Он может получать все в качестве реквизитов и передавать ему обработчики вызовов.

Ответ №1:

Вы пробовали что-то еще внутри useState, например

  const [roleName, setRoleName] = useState("Hello");
  

посмотрите, печатает ли console.log имя роли "Hello" , и если да, просто укажите это, чтобы исправить ваше состояние:

 const role = props.roleInfo.role_name;
useEffect(() => setRoleName(role), [role]);