React Modal Updater 在 setState() 之后不刷新页面

React Modal Updater doesn't refresh page after setState()

有人建议我浅拷贝一个对象,因为仅使用=符号拷贝状态对象会“指向”旧状态。

现在,我明白了:我的屏幕上有一个列表,上面的每个项目都是状态数组中的一个元素。每个项目都有自己的“更改”按钮,当按下该按钮时,将打开一个反应模式,将所有项目信息输入文本,这样我就可以更改它并按“更新”按钮模态。按下后,模式应该关闭,列表应该刷新,但最后这件事不会发生。

这是完整的组件代码:

import React, { useContext, useEffect, useState } from 'react';
import { ThemeContext } from '../../providers/ThemeContext';
import axios from '../../services/axios';
import './styles.css';
import Modal from 'react-modal';
import { Divider } from '@material-ui/core';

Modal.setAppElement('#root')
function AllInOneProjectPage() {
    const [projects, setProjects] = useState<any[]>([]);
    const { theme } = useContext(ThemeContext);
    const [isModalOpen, setIsModalOpen] = useState(false);
    const [id, setId] = useState('');
    const [title, setTitle] = useState('');
    const [url, setUrl] = useState('');
    const [techs, setTechs] = useState<any[]>([]);
    
    const openModal = (idModal: string, title: string, url: string, techs: any[]) => {
        setId(idModal);
        setTitle(title);
        setUrl(url);
        setTechs(techs);
        setIsModalOpen(true);
    }

    const closeModal = () => {
        setIsModalOpen(false);
    }

    const updateProjects = async () => {
        const res = await axios.get('list');
        res.data.status === 1 && setProjects(res.data.projects);
    };
    
    useEffect(() => {
        updateProjects();
    }, []);

    const onUpdateProject = async () => {
        const newTitle = (document.getElementById(`modal_title_${id}`) as HTMLInputElement)?.value;
        const newUrl = (document.getElementById(`modal_url_${id}`) as HTMLInputElement)?.value;
        const techsArray = (document.getElementById(`modal_techs_${id}`) as HTMLInputElement)?.value;
        const filteredTechs = techsArray.split(',').filter(tech => {
            if(tech !== '')
                if(tech !== ' ')
                    return tech;
        });
        
        await axios.patch(`update/${id}`, { title: newTitle, url: newUrl, techs: filteredTechs });
        setProjects(projects.map(p => id === p.id ? { ...p, title: newTitle, url: newUrl, techs: filteredTechs } : p));
        setIsModalOpen(false);
        console.log(projects.map(p => id === p.id ? { ...p, title: newTitle, url: newUrl, techs: filteredTechs } : p));
    };
    
    return (
        <div style={{
            width: '100vw',
            minHeight: 'calc(100vh - 64px)',
            backgroundColor: theme.bg,
            margin: '0',
            display: 'flex',
            justifyContent: 'space-around',
            flexWrap: 'wrap'
        }}>
            { projects && projects.map((p, i) => <div key={p.id} style={{
                display: 'flex',
                flexDirection: 'column',
                width: '550px',
                height: '300px',
                border: '1px solid blue',
                margin: '50px'
            }}>
                <div style={{ backgroundColor: 'green', height: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'space-around' }}>
                    <div style={{display: 'flex', flexDirection: 'column', width: '80%'}}>
                        <h4>Title:</h4>
                        <input type="text" name="title" disabled defaultValue={ p.title }/>
                    </div>
                    <div style={{display: 'flex', flexDirection: 'column', width: '80%'}}>
                        <h4>URL:</h4>
                        <input type="text" name="url" disabled defaultValue={ p.url }/>
                    </div>
                    <div style={{display: 'flex', flexDirection: 'column', width: '80%'}}>
                        <h4>Techs:</h4>
                        <input type="text" name="techs" disabled defaultValue={ p.techs.map((t: any) => t) }/>
                    </div>
                    <a style={{textDecoration: 'underline', color: 'darkblue', cursor: 'pointer' }} onClick={() => openModal(p.id, p.title, p.url, p.techs)}>Alterar</a>
                </div>
            </div>) }

            <Modal isOpen={isModalOpen} style={{
                content: {
                    display: 'flex',
                    flexDirection: 'column',
                    maxWidth: '450px',
                    height: '280px',
                    margin: 'auto',
                    backgroundColor: 'lightgrey'
                },
                overlay: {
                    backgroundColor: 'rgba(0, 0, 0, 0.85)'
                }
            }}>
                <div className="modal-header" style={{ display: 'flex', justifyContent: 'space-between' }}>
                    <h1>Edit { title }</h1>
                    <button onClick={closeModal} style={{ width: '90px', height: '20px', margin: 'auto 0' }}>Close</button>
                </div>
                <Divider />
                <div style={{ width: '80%', margin: '10px auto 0 auto'}}>
                    <h2>Title: </h2>
                    <input type="text" name="title" id={`modal_title_${id}`} defaultValue={ title } style={{ width: '90%' }}/>
                </div>
                <div style={{ width: '80%', margin: '10px auto 0 auto'}}>
                    <h2>URL: </h2>
                    <input type="text" name="title" id={`modal_url_${id}`} defaultValue={ url } style={{ width: '90%' }}/>
                </div>
                <div style={{ width: '80%', margin: '10px auto 0 auto'}}>
                    <h2>Techs: </h2>
                    <input type="text" name="title" id={`modal_techs_${id}`} defaultValue={ techs } style={{ width: '90%' }}/>
                </div>
                <button onClick={ () => onUpdateProject() } style={{ width: '90px', margin: '10px auto 0 auto' }}>Update</button>
            </Modal>
        </div>
    );
}

export default AllInOneProjectPage;

问题

我觉得这个问题很简单;您已将 defaultValue 用于 AllInOneProjectPage 组件上的禁用输入。当一个输入使用 defaultValue 属性时,它使它们成为不受控制的输入,这意味着它们不响应任何外部变化,也不受任何状态控制。

解决方案

使用 value 道具将这些输入转换为受控输入。

{projects &&
  projects.map((p, i) => (
    <div key={p.id} style={outerDivProject}>
      <div style={projectFields}>
        <h4>Title:</h4>
        <input type="text" name="title" disabled value={p.title} /> // <-- now controlled
      </div>

      <div style={projectFields}>
        <h4>URL:</h4>
        <input type="text" name="url" disabled value={p.url} /> // <-- now controlled
      </div>

      <div style={projectFields}>
        <h4>Techs:</h4>
        <input
          type="text"
          name="techs"
          disabled
          value={p.techs.map((t: any) => t)} // <-- now controlled
        />
      </div>

      <p
        style={changeButtonStyles}
        onClick={() => openModal(p.id, p.title, p.url, p.techs)}
      >
        Alterar
      </p>
    </div>
  ))}

分叉演示