getting innerHTML from a div after changes through contentEditable in React - reactjs

thanks for taking the time to look at this.
I am struggling to find out how to make this work specifically in react.
I have used contentEditable to get the div element to be editable and then i have used Refs to make the div reference its innerHTML. But the information does not seem to be put into the state of body.
The ultimate aim is to have it saved in a database, and then loaded to replace the div.
code:
import React, {useState, useRef} from "react";
import "./styles.css";
export default function App() {
let myRef= useRef()
const [body, setBody] = useState("");
let click = () => {
setBody(myRef.innerHTML)
}
return (
<div className="App">
<h1 ref={myRef}>Hello CodeSandbox</h1>
<div></div>
<h1 contentEditable={true}> rewrite me!</h1>
<button onClick={click}> CLICK!</button>
<h1>{body}</h1>
</div>
);
}
sandbox
https://codesandbox.io/s/wispy-glitter-nfym4?file=/src/App.js

Access the innerHTML using myRef.current.innerHTML.
From the docs
When a ref is passed to an element in render, a reference to the node becomes accessible at the current attribute of the ref.
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/#babel/standalone/babel.min.js"></script>
<div id="root"></div>
<script type="text/babel">
function App() {
let myRef = React.useRef();
const [body, setBody] = React.useState("");
let click = () => {
setBody(myRef.current.innerHTML);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<div></div>
{/* I think you misassigned your `myRef`, shouldn't it be on this h1? */}
{/* suppressContentEditableWarning=true, to suppress warning */}
<h1 ref={myRef} contentEditable={true} suppressContentEditableWarning={true}> rewrite me!</h1>
<button onClick={click}> CLICK!</button>
<h1>{body}</h1>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
</script>

Related

getting [object Object] instead of my jsx element in react

i want to add paragraph element to my div element on first mount .But on browser ,i see [object Object]. Also,i want to know why i am getting [object Object] on my browser instead of text 'hi' . what changes to be made for 'hi' text instead of [object Object].
import './App.css';
import {useEffect,useState} from 'react'
function App() {
let h=(<p>hi</p>);
useEffect(()=>{
document.getElementById('i').append(h)
},[])
return (
<div id='i' className="App">
</div>
);
}
export default App;
Never use native DOM methods in React unless there's no other option.
Here, you should interpolate the <p> into the returned JSX. Use a boolean state to indicate whether the initial mount has completed.
const { useEffect, useState} = React;
function App() {
const [mounted, setMounted] = useState(false);
useEffect(()=>{
setMounted(true);
},[])
return (
<div id='i' className="App">
{
mounted && <p>hi</p>
}
</div>
);
}
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>
you are getting [object][object] because you are passing object in h.
You can change the code as below to get your desired output,
let h='<p>hi</p>)';
document.getElementById("i").innerHTML += h
Full code below,
//import './App.css';
import React, {useEffect,useState} from 'react'
function App() {
let h='<p>hi</p>)';
useEffect(()=>{
document.getElementById("i").innerHTML += h
},[])
return (
<div id='i' className="App">
</div>
);
}
export default App;

using state in dangerouslySetInnerHTML react component

I have a string about html builded by react.
I'm trying to implement render this html string through react, and looking for solution to manage state.
below is simple example.
const App = (props) => {
let code = '<b>Will This Work?</b>';
return (
<div dangerouslySetInnerHTML={ {__html: code} }>
</div>
);
}
I want to manage the state of component rendered with dangerouslySetInnerHTML option.
Can i get any ideas about how approach?
What is dangerouslySetInnerHTML?
It is a way to set the children of the component (as text/html).
Can state be used in it?
Yes state can be used with it; However, the innerHTML MUST be vanilla HTML, NOT JSX. An example of this can be seen below:
(click the button to change the state from text to a red div)
The way this is working is because the state can be inserted into a string literal which will then be handled as HTML encoded text.
const App = (props) => {
const [state,setState] = React.useState("something I set with state");
let code = `<b>Will This Work? ${state}</b>`;
return (
<React.Fragment>
<button onClick={()=>{setState("<div style=\"background-color: red; width:50px; height:50px;\"><div>")}}>Click to change state from pure text to an html element</button>
<div dangerouslySetInnerHTML={ {__html: code} }>
</div>
</React.Fragment>
);
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.querySelector('.react')
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>
Should you do this?
Probably not. There is a reason it is called dangerouslySetHTML and not safelySetInnerHTML.
See more in the React Docs

Using the right pattern in react to add an id without passing it directly using props but through map?

I have this code on Codesandbox the goal is to be able to pass 5 Divs, on load using use Effect.
and a second option to add a div on click when if the user feels like it. the code is partially working, but it has a anti-patter issue which is putting the component in the state instead of changing the state using map to pass the changes..
please take a look I would like to hear your opinion on this, what I do understand is importing the Div element like this could affect performance, I want to avoid bad practice as much as possible.
import React, { useEffect, useState } from "react";
import Div from "./Div";
import "./styles.css";
import { v4 as uuidv4 } from "uuid";
export default function App() {
useEffect(() => {
// on start add 5 divs in to the local state Array on the frist load
});
const [div, setDiv] = useState([]);
const addDiv = () => {
// add an extra div on click if needed with id using the right pattern
setDiv([...div, <Div id={uuidv4()} />]);
};
return (
<div className="App">
{div}
<button onClick={addDiv} type="button">
Click Me!
</button>
</div>
);
}
//Dev dependencise
"uuidv4": "6.2.12"
Codesandbox
Putting JSX elements into state is a bad idea because they won't be reactive - you won't be able to (reliably) pass down state, state setters, and other useful things as props.
It's not so much a performance issue as a code maintainability issue - if you add additional functionality to your Div component and to your App you may find that your current approach won't work due to stale values that the JSX elements in state close over.
If you need the ability to delete a value, use the index of the div in the array and pass it down as needed. For a quick and dirty example:
function App() {
const [texts, setTexts] = React.useState([]);
const [text, setText] = React.useState('');
React.useEffect(() => {
setTexts(['a', 'b', 'c', 'd', 'e']);
}, []);
const addDiv = () => {
setTexts([...texts, text]);
setText('');
};
return (
<div className="App">
{
texts.map((text, i) => (
<div>
<span>{text}</span>
<button onClick={() => setTexts(texts.filter((_, j) => j !== i))}>delete</button>
</div>
))
}
<button onClick={addDiv} type="button">
Click Me!
</button>
<input value={text} onChange={e => setText(e.target.value)} />
</div>
);
}
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>
Just add id in the array and using map to render
{div.map(id => (
<Div key={id} id={id} />
))}
const addDiv = () => {
setDiv([...div, uuidv4()]);
};

document.getElementById() equivalent in React 2020

I have a component called Button.js that has a button that when clicked i simply would like to know if i am accessing the a div in another component called Timer.js. In vanilla javascript i would simply use document.getElementById() to capture the DOM node. How is this done in React?
I came across callback-refs in the docs but it isn't working. If using a ref isn't the React way of accessing DOM elements please refer me to the best way to do this. thanks in advance.
Button.js
function Button() {
const getHtml = () => {
const node = test.current;
console.log(node);
}
return (
<button onClick={getHtml}>GetHtml</button>
)
}
Timer.js
function Timer() {
const test = useRef(null);
return (
<div ref={test}>... </div>
<Button />
}
I would not use a reference to check if a component is rendered inside of another one.
You could get what you're looking for with createContext and useContext.
(It could work like you tried it. If you'd pass the ref to the button as a prop.)
With the context: You create a TimerContext.Provider in your Timer component and in your button you can check with useContext(TimerContext) if the expected key is in the object. If it's not there then the button is not inside of your Timer.
Please have a look at the snippet below or in the following Codesandbox.
//import React, { useContext, createContext } from "react";
//import "./styles.css";
const { useContext, createContext } = React;
const ContainerContext = createContext({
isInContainer: null
});
const Container = () => {
return (
<ContainerContext.Provider value={{ isInContainer: true }}>
<p>
In container:
<Button />
</p>
</ContainerContext.Provider>
);
};
const Button = () => {
const { isInContainer } = useContext(ContainerContext);
console.log(isInContainer);
const isInside = () => {
alert(isInContainer ? "clicked inside" : "not in container");
};
return <button onClick={isInside}>Click me</button>;
};
function App() {
return (
<div className="App">
<Container />
<Button />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
rootElement
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
Update 15.04.2020
The question was not clear to me at first but now I understand the use-case. The idea is to have an Editor component where you're writing markup that can be used to generate a copied snippet view and/or a html markup output.
For this the best is to use a reference to the Editor component and pass it as prop to the preview/output component - it would be also possible with a context but passing it is easier.
Like in the following Sandbox.

Does react element itself has inner text, inner html?

Learning react, i see element examples like this,
const element = <Welcome name="Sara" />;
Does it support ?
const element = <Welcome name="Sara">good day </Welcome>;
If so, how to get "good day" while "name" belongs to props ?
Thanks !
The content of the element is exposed through the children prop.
const Welcome = (props) => {
return <div>
<p>Hello {props.name}!</p>
{props.children}
</div>
};
ReactDOM.render(<Welcome name="Sara">good day </Welcome>, document.body);
<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>
React has children prop out of the box, which accepts nested content inside Component
function Welcome (props) {
console.log('props.name', props.name)
console.log('props.children', props.children)
return <h1>{props.name} {props.children}</h1>
}
function App () {
return (
<div>
<Welcome name="Sara">good day </Welcome>
<Welcome name="Sara" />
</div>
);
}
ReactDOM.render(<App />, document.body)
<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>

Resources