#javascript #reactjs
#javascript #reactjs
Вопрос:
Допустим, у меня есть таблица:
<div>
<tr>
<td>
<p id='1' className="foo"> Boom </p>
</td>
<td>
<p id='2' className="foo"> Bang </p>
</td>
<td>
<p id='3' className="foobar"> Pew Pew </p>
</td>
</tr>
</div>
Я хочу, чтобы данные внутри него были доступны для редактирования на месте. Таким образом, я хочу заменить <p>
элемент на an <input>
, а затем <p>
снова заменить его, но с новым значением. Я делал это с помощью jQuery, а теперь сделал это с помощью того, что мне кажется простым JS, но с React. Код выглядит следующим образом:
import React, { Component } from "react";
import "./App.css";
class App extends Component {
handleClick(e) {
if (e.target.className === 'foo'){
let element = document.getElementById(e.target.id)
let element_value = element.innerText
let parent_element = element.parentNode
let new_element = document.createElement('input')
parent_element.removeChild(element)
parent_element.appendChild(new_element)
new_element.setAttribute('class', 'input')
new_element.setAttribute('id', e.target.id)
new_element.setAttribute('value', element_value)
} else if (e.target.className === 'input') {
let element = document.getElementById(e.target.id)
let element_value = element.value
let parent_element = element.parentNode
let new_element = document.createElement('p')
parent_element.removeChild(element)
parent_element.appendChild(new_element)
new_element.setAttribute('class', 'foo')
new_element.setAttribute('id', e.target.id)
new_element.innerText = element_value
}
};
componentDidMount() {
document.addEventListener('dblclick', this.handleClick)
}
componentWillUnmount() {
document.removeEventListener('dblclick', this.handleClick)
}
render() {
return (
<div>
<tr>
<td>
<p id='1' className="foo"> Boom </p>
</td>
<td>
<p id='2' className="foo"> Bang </p>
</td>
<td>
<p id='3' className="foobar"> Pew Pew </p>
</td>
</tr>
</div>
)
}
}
export default App
Однако это не кажется мне хорошей практикой. Не могли бы вы дать мне советы о том, как улучшить / изменить мой подход? Спасибо.
Ответ №1:
Вероятно, лучшим подходом является создание управляемого компонента, который обрабатывает всю логику для редактируемой ячейки и сохраняет значения в родительской. Я создал изолированную среду, которую вы можете посмотреть здесь, но я также добавлю код сюда.
Таким образом, компонент cell предоставляет все необходимые элементы представления, а родительский элемент управляет логикой и данными для всех ячеек.
Таким образом, редактируемая ячейка обрабатывает функциональность переключения между представлениями:
const EditableCell = ({ id, onEdit, className, value }) => {
const [isEditing, setIsEditing] = useState(false);
const onClick = useCallback(() => {
setIsEditing(true);
}, []);
const onFinishedEditing = useCallback(() => {
setIsEditing(false);
}, []);
const onKeyDown = useCallback(
(e) => {
if (e.key === "Enter") {
onFinishedEditing();
}
},
[onFinishedEditing]
);
return (
<td>
{isEditing ? (
<input
value={value}
onChange={(e) => onEdit(e.target.value, id)}
onBlur={onFinishedEditing}
onKeyDown={onKeyDown}
autoFocus
/>
) : (
<p {...{ id, className, onClick }}>{value}</p>
)}
</td>
);
};
А затем приложение сохраняет данные ячеек и отображает EditableCell
для каждой из них:
export default function App() {
// This stores the cells values and properties, you can
// add or remove cells here are needed
const [cellValues, setCellValues] = useState([
{ id: "1", class: "foo", value: "Boom" },
{ id: "2", class: "foo", value: "Bang" },
{ id: "3", class: "foobar", value: "Pew Pew" }
]);
const onEdit = (value, id) => {
setCellValues(
cellValues.map((cellVal) =>
cellVal.id === id ? { ...cellVal, value } : cellVal
)
);
};
return (
<div>
Click a cell to edit
<tr>
{cellValues.map((cellVal) => (
<EditableCell
id={cellVal.id}
value={cellVal.value}
className={cellVal.class}
onEdit={onEdit}
/>
))}
</tr>
</div>
);
}
Это может не совсем соответствовать желаемой функциональности, но должно дать вам отправную точку
Комментарии:
1. Спасибо, это мне поможет 🙂
Ответ №2:
Я пообещал себе, что буду делать добрые дела хотя бы по одному в день.Я знаю, что вы пишете в классе, но я так сильно придерживаюсь крючков, что … извините, чувак: P
он вызывает onChange, когда во время редактирования вы нажимаете enter.
import React, { Component, useEffect, useMemo, useRef, useState } from "react";
import "./styles.css";
const Td = ({ children, editable = false, onChange, className, id }) => {
const cell = useRef();
const [edit, setEdit] = useState(false);
const [value, setValue] = useState(() => {
while (typeof children !== "string") {
children = children.props.children;
}
return children;
});
const [oldValue, setOldValue] = useState(value);
useEffect(() => {
if (!cell.current) return;
const onEditMode = () => editable amp;amp; setEdit(true);
const target = cell.current;
target.addEventListener("click", onEditMode);
return () => {
target.removeEventListener("click", onEditMode);
};
}, [cell, setEdit, editable]);
const paragraph = useMemo(() => (
<p id="1" className="foo">
{value}
</p>
),[value]);
const input = useMemo(() => {
const update = (value) => {
setEdit(false);
if (onChange amp;amp; typeof onChange === "function") {
onChange({
id,
newValue: value,
oldValue: oldValue
});
setOldValue(value);
}
}
return (
<input
value={value}
onChange={ e => setValue(e.target.value)}
onKeyDown={ e => e.key === "Enter" amp;amp; update(value)}/>
)
},[value, setEdit, onChange, id, oldValue, setOldValue]);
return (
<td ref={cell} className={className}>
{edit ? input : paragraph}
</td>
);
};
class App extends Component {
componentDidMount() {
}
componentWillUnmount() {
}
tableCellValueChange({ id, newValue, oldValue }) {
console.log(
`table cell id: ${id} value changed from ${oldValue} to ${newValue}`
);
}
render() {
return (
<div>
<table>
<thead></thead>
<tbody>
<tr>
<Td
onChange={this.tableCellValueChange}
id="special"
editable>
<p>Bang </p>
</Td>
<Td onChange={this.tableCellValueChange} editable>
<p>Bang</p>
</Td>
<Td editable={false} className="forbar">
Pew Pew
</Td>
</tr>
</tbody>
</table>
</div>
);
}
}
export default App;
здесь у вас есть живой пример песочницы
Комментарии:
1. спасибо, приятель, хуки — действительно интересная функция.