#javascript #reactjs #react-hooks
#javascript #reactjs #реагирующие хуки
Вопрос:
Я пытаюсь создать повторно используемый компонент, который изменяет его поведение — по сути, рендеринг либо в SVG, либо в Canvas. В настоящее время я пытаюсь сделать это, обернув его в HoC (однако я пытаюсь использовать React hooks), так что все это выглядит немного плоско.
Редактировать с дополнительной информацией
При текущем подходе (HoC возвращает функцию) Я получаю следующую ошибку:
Функции недопустимы как дочерние элементы React. Это может произойти, если вы возвращаете компонент вместо из render . Или, может быть, вы хотели вызвать эту функцию, а не возвращать ее.
Если я удалю вызов функции в HoC,:
React.jsx: недопустимый тип — ожидал строку (для встроенных компонентов) или класс / функцию (для составных компонентов), но получил: object
Я видел пример преобразования HoC в React hooks, но я изо всех сил пытаюсь понять, как я мог бы преобразовать это — если это вообще возможно, и интересно, может ли кто-нибудь дать мне указатель? Возможно, я попытался спроектировать это неправильно, так как это кажется довольно сложным вариантом использования.
Итак, на данный момент у меня есть эти 2 HoC, которые, я чувствую, мне нужно как-то реорганизовать, чтобы использовать хуки, называемые withSVG
and withCanvas
. Они настраивают другой DOM для представления, и, что важно, у одного есть вызываемая функция renderLoop
, которую необходимо вызвать из компонента Scatter ниже.
Немного странно, потому Scatter
что теперь это просто бизнес-логика (использование зависимостей в useEffect), на самом деле ему не нужно возвращать какой-либо DOM, потому что он просто манипулирует DOM родительского HoC. Однако я ожидаю еще много таких компонентов, как Scatter
в будущем, поэтому не хочу переносить эту логику в HOC (если они вообще подходят для этого).
const duration = 2000;
const withSVG = ({ width, height, ...props }) => WrappedComponent => {
const layer = useRef(null);
return (props) => (
<g width={width} height={height} ref={layer}>
<WrappedComponent {...props} layer={layer} />
</g>
);
};
const withCanvas = ({ width, height, ...props }) => WrappedComponent => {
const layer = useRef(null);
const canvas = useRef(null);
const renderLoop = getRenderLoop(canvas.current, width, height);
return (props) => (
<React.Fragment>
<custom ref={layer}/>
<canvas width={width} height={height} ref={canvas}></canvas>
<WrappedComponent {...props} layer={layer} renderLoop={renderLoop} />
</React.Fragment>
);
};
const Scatter = ({ data, layer, renderLoop }) => {
useEffect(() => {
if (!layer.current) { return; }
// D3 data join
const join = d3
.select(layer.current)
.selectAll("circle")
.data(data, d => d.key);
// Shrink the circles to 0 size
const exit = join.exit()
.transition("radius")
.duration(duration)
.attr("r", 0)
.remove();
const enter = join.enter()
.append("circle")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", 0)
.style("fill", d => d.color);
const update = enter
.merge(join)
.transition("radius")
.duration(duration)
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", 30)
.style("fill", d => d.color);
if (renderLoop) {
renderLoop(exit, update);
}
}, [layer, data]);
return null;
};
const CanvasScatter = withCanvas(Scatter);
const SVGScatter = withSVG(Scatter);
// index.js
const width = 400;
const height = 400;
const App = () => {
const [data, setData] = useState([
{
key: 1,
x: 50,
y: 50,
color: "red"
},
{
key: 2,
x: 150,
y: 150,
color: "blue"
},
]);
setTimeout(() => {
setData([
{
key: 1,
x: 100,
y: 100,
color: "orange"
},
{
key: 3,
x: 150,
y: 50,
color: "green"
},
]);
}, 3000);
return (
<div>
<svg width={width} height={height}>
<SVGScatter width={width} height={height} data={data} />
</svg>
<CanvasScatter width={width} height={height} data={data} />
</div>
);
// return <div>Hello React,Webpack 4 amp; Babel 7!</div>;
};
ReactDOM.render(<App />, document.querySelector("#root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="root"></div>
Комментарии:
1. Я не уверен, что вы подразумеваете под «вы не можете вернуть функциональный компонент из HoC»… Компоненты более высокого порядка используют компонент react и возвращают компонент react. Является ли это классовым или функциональным компонентом, не имеет значения.
2. @DrewReese ах, может быть, я неправильно истолковал свою ошибку или неправильно выполнил свою HoC.
Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render
Я обновился, чтобы попытаться отразить состояние, в котором я нахожусь, немного больше — возможно, мой подход к чему-то приводит
Ответ №1:
Проблема
Я думаю, что вы неправильно сформировали свои компоненты более высокого порядка. Похоже, вы определили их для использования некоторых реквизитов, а затем компонента для переноса.
const withSVG = ({ width, height, ...props }) => WrappedComponent => {...}
но вы вызываете их, как и ожидалось
const SVGScatter = withSVG(Scatter);
Здесь Scatter
сначала используется компонент, и HOC пытается извлечь из него значения и возвращает функцию для использования компонента, который является undefined
. Я думаю, что это вызывает ошибку, которую вы видите.
Решение
В зависимости от того, как вы используете оформленные компоненты
<SVGScatter width={width} height={height} data={data} />
<CanvasScatter width={width} height={height} data={data} />
Я думаю, вы имели в виду width
деструкцию и height
из внутреннего компонента, того, который возвращается HOC.
const withSVG = WrappedComponent => ({ width, height, ...props }) => {
const layer = useRef(null);
return (
<g width={width} height={height} ref={layer}>
<WrappedComponent
layer={layer}
{...props} // <-- data passed here
/>
</g>
);
};
const withCanvas = WrappedComponent => ({ width, height, ...props }) => {
const layer = useRef(null);
const canvas = useRef(null);
const renderLoop = getRenderLoop(canvas.current, width, height);
return (
<React.Fragment>
<custom ref={layer}/>
<canvas width={width} height={height} ref={canvas}></canvas>
<WrappedComponent
layer={layer}
renderLoop={renderLoop}
{...props} // <-- data passed here
/>
</React.Fragment>
);
};
Комментарии:
1. Спасибо, вы были абсолютно правы. Замена их и несколько других исправлений кода, и это стоит того. Очевидно, я немного запутался в сообщениях об ошибках и подумал, что я нахожусь на пути в никуда, чего не было: D