Why Won't This Render as JSX? - reactjs

I have a string variable, with this HTML code like so:
this.state.contents:
<p>This Handbook is designed to provide you with information about your employment at {this.props.clientName} and its affiliates and subsidiaries (referred to throughout this Handbook as “{this.props.clientName}” or the “Company”).</p>
I pass it to a child component like this:
<Policy key={filteredPolicy.id} id={filteredPolicy.id} contents={filteredPolicy.contents} clientName={this.state.client}/>
The child component is this:
<Card.Body className="content" >{this.props.contents}</Card.Body>
But in the component, it renders as the string and just shows all the HTML tags. How do I get it to actually render correctly as HTML code? A second issue is the variables of {this.props.clientName} also do not show up... it just literally renders {this.props.clientName} as a string. It all works correctly if I just do it as HTML. But when I try to do it this way, no dice.

While you can still use dangerouslySetInnerHTML to achieve it like this, it's not recommended
function Child({ data }) {
return <div dangerouslySetInnerHTML={{__html: data}}></div>;
}
const test = "test-data";
export default function App() {
const [data] = useState(`<div>${test}</div>`); // note the ${} (template literal) here instead of {}, because this is a string, not React component
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<Child data={data} />
</div>
);
}
Live example
Define a renderData function inside Parent component to render your html, pass it to Child as a prop, and call that renderData function here. You can use your state, props inside that renderData function as you want.
So the above example can be rewrite like this
function Child({ renderData }) {
return <div>{renderData()}</div>;
}
const test = "test-data";
export default function App() {
const renderData = () => {
return (
<div>{test}</div>
)
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<Child renderData={renderData} />
</div>
);
}
Live example

Related

React - Call function from non-parent component with state from separate component

I'm attempting to call a function from an AppBar with the state from a child component, like so
// App.js
<BrowserRouter>
<Nav />
<Routes>
<Route exact path={"/"} element={<MyComponent/>}/>
<Routes>
</BrowserRouter>
//Nav.js
function Nav() {
return (
<div>
<h1>Hello World</h1>
<button onClick={logChildState}>Get State</button>
</div>
)
}
// MyComponent.js
function MyComponent() {
const [someState, setSomeState] = useState({
Some state values....
})
return (
<div>
<input />
...more components...
</div>
)
}
logChildState() == "Some state values...."
The goal is to have the AppBar have a button with a function call that captures the state of MyComponent. As this is a simplified example, I will just say that the state should exist in the child, and it's not possible to hoist the state to App.js - because of this, I don't see a way to accomlish what I'm looking for easily, I've looked at possibly achieving this using context or an observable but it would be quite messy.
I'm wondering what the best way to tackle this kind of issue would be, or if my best choice would just be to have the "button" in Nav.js in the MyComponent.js.
Thanks
You can add the function as a prop like this:
//Nav.js
function Nav({logChildState}) {
return (
<div>
<h1>Hello World</h1>
<button onClick={() => logChildState('send this message')}>Get State</button>
</div>
)
}
And in your app Component you can simply take it as a prop like this:
<Nav logChildState = {logChildState}/>
and if you want to print the message comming from Nav component simply do this in App.js
const logChildState = (message) => {
console.log(message);
}
Hope that helps!

Change the state of another component using functionnal components in React

I have a button in my header that has to switch the state of the lateral menu of the page (to know if it has to be shown or not). I found out on Internet how to do so using Class Components, but not with Functional Components, do you have any idea on how to achieve that ?
Here's a simplification of my actual code in order to reproduce my issue (I removed all useless code).
App.js :
function App() {
return (
<div className="App">
<Header />
<div>
<LateralMenu />
</div>
</div>
);
}
The Header component :
function Header() {
const [lateralIsOpen, setLateralIsOpen] = useState(true);
function changeLateralMenu() {
setLateralIsOpen(!lateralIsOpen);
}
return (
<header>
<div onClick={ changeLateralMenu }>
</header>
);
}
And the LateralMenu component :
function Header() {
const [lateralIsOpen, setLateralIsOpen] = useState(true);
return (
<section>
{ lateralIsOpen ? "open" : "closed" }
</section>
);
}
I tried (but maybe not correctly) to declare the lateralIsOpen State in the App component and sending it through props to my children componenents (Header & LateralMenu).
I also tried looking at this question (and a few others) which is pretty similar, but don't see how I can apply it in my case because (as I understand) he uses a button in the parent component, that changes a state in the parent component, and then send it to the children through props... Where in my case, the button to switch it is already in a child.
I'd suggest you to move the state out of the LateralMenu to the parent (App) and then just pass the toggle function to the Header to toggle it.
export default function App() {
const [lateralIsOpen, setLateralIsOpen] = useState(true);
return (
<div className="App">
<Header toggleLateral={() => setLateralIsOpen((prev) => !prev)} />
<div>
<LateralMenu isOpen={lateralIsOpen} />
</div>
</div>
);
}
function Header({ toggleLateral }) {
function changeLateralMenu() {
toggleLateral();
}
return (
<header>
<div onClick={changeLateralMenu}>click</div>
</header>
);
}
function LateralMenu({ isOpen }) {
return <section>lateral is {isOpen ? 'open' : 'closed'}</section>;
}
Codesandbox: https://codesandbox.io/s/eager-heyrovsky-z75njd

Is there a way to conditionally create/clone an element and assign it to a variable in React?

I have created variable like this:
const Component = useMemo(() => {
if (selectable || multi) {
return React.cloneElement('div', {
className: classes.contentDetail,
});
}
return React.cloneElement(RouterLink, {
className: classes.contentDetail,
to: { pathname: '/abcd/xyz' },
});
}, [selectable, multi]);
But when I use this component in JSX, it throws a react invalid element type error?
<Component>
<p>Hello World!</p>
</Component>
I can solve my problem using another approach, but I just want to know if there is a way to conditionally assign a component to a variable while passing different props.
I think there are a couple issues here, first you are passing a string to clone which should be a proper html element or component React.cloneElement(<div></div>, ...) for example.
Secondly the syntax is as follows:
React.cloneElement(element, [props], [...children])
So you can pass children when cloning, which will already return a component rendered with the props/children you passed in, So to use that in the render function of your component (after passing children):
return Component
Link to docs
As I suggested in the comments just use {Component} instead of <Component></Component> because of what gets returned from React.cloneElement.
Also, use <div /> instead of "div" as DannyMoshe says.
export default function App() {
const Component = useMemo(() => {
return React.cloneElement(<div />, {
className: "contentDetail",
children: <p>Hello World!</p>
});
}, []);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
{Component}
</div>
);
}

How a child component can get several props/data from two separate parent components

In one component, why can't I receive two/multiple props from separate components?
I'm getting this error:
TypeError: props.name is not a function.
What am I doing wrong?
Here is how I tried to do it -
function Welcome() {
const [searchString, setString] = useState('');
return (
<div className='App'>
<Header name={searchString} />
</div>
);
}
function Home() {
const [numbers,setNumbers] = useState('');
retrun(
<Header moneyAmount={numbers} />
)
}
function Header(props) {
return(
<div>
{props.name}
{props.moneyAmount}
</div>
)
}
All tries this way or as function Header({name, moneyAmount}){}
field.
Is there's a way with createContext or useContext ?
I tried with useContext but I think it's collide with the props, still gives an error.
You've 3 components: Welcome, Home and Header
Welcome is using the Header component and passing it a name prop, so it will only render the name you passed (which you gave the wrong variable, should be name={searchString} instead of name={setString}).
Home is also using the Header component and passing it a moneyAmount prop,
so it will only render the moneyAmount variable.
Welcome and Home have no relation, so the Header component in each one of them dosen't know about the props passed from the other. If you want to pass multiple variables, you must pass directly to the component like so:
function Home(props) {
const [numbers,setNumbers] = useState('');
return (
<Header moneyAmount={numbers} name={prop.searchString} />
)
}
If you want to pass data to a child from 2 different components, you do it like so:
function Welcome() {
const [searchString, setString] = useState('');
return (
<div className='App'>
<Home name={searchString} />
</div>
);
}
function Home(props) {
const [numbers,setNumbers] = useState('');
return (
<Header moneyAmount={numbers} name={props.name} />
)
}
function Header(props) {
return(
<div>
{props.name}
{props.moneyAmount}
</div>
)
}

How can i pass a pirce of HTML content into a stateless react component?

I want to make a simple React component that wraps the given HTML content into a tag that serves as a context in which the content is displayed.
function Content(content) {
return <div>{content}</div>;
}
<Content>
<span>Hey!</span>
</Content>
What am i doing wrong?
function Content(props) {
return <div>{props.children}</div>;
}
Using ES6 syntax:
const Content = ({ children }) => <div>{children}</div>
Simple React component that wraps the given HTML content.
var content = "<Content><span>Hey!</span></Content>"
function Content() {
return (
<div className="content" dangerouslySetInnerHTML={{__html:
content}}>
</div>
);
}

Resources