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>
))}
分叉演示
有人建议我浅拷贝一个对象,因为仅使用=
符号拷贝状态对象会“指向”旧状态。
现在,我明白了:我的屏幕上有一个列表,上面的每个项目都是状态数组中的一个元素。每个项目都有自己的“更改”按钮,当按下该按钮时,将打开一个反应模式,将所有项目信息输入文本,这样我就可以更改它并按“更新”按钮模态。按下后,模式应该关闭,列表应该刷新,但最后这件事不会发生。
这是完整的组件代码:
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>
))}