The CMS passes a variable as data-rest-url attribute to the React.js App:
<div id="reactjs-root" data-rest-url="http://my-ip-addess:8080/Rest-api-here">...</div>
If I add jQuery to my React.js App, then I can simply:
componentWillMount() {
const $reactRoot = $('#reactjs-root');
const restUrl = $reactRoot.attr('data-rest-url');
}
But adding jQuery just for this? How would you pass some variable from a CMS to your Single Page React App and read / parse / get it with react.js?
Consider passing your data attributes to your component as props instead of hard coding the root element ID within the component itself.
Rendering:
var rootElement = document.getElementById('reactjs-root');
ReactDOM.render(
<YourComponent resturl={rootElement.getAttribute('data-rest-url')}></YourComponent>,
rootElement
);
Within the component you can access the injected url:
componentWillMount() {
console.log(this.props.resturl)
}
This makes for a more reusable component that is decoupled from a specific element ID.
const reactRoot = document.getElementById('reactjs-root');
const restUrl = reactRoot.getAttribute('data-rest-url');
Also, avoid using $ in your variable name. You're likely to run into a lot of libraries that conflict with the $ you have used as a variable.
Related
I'm working on the freeCodeCamp drum machine app. In my app with function arrow components, I set state of display with the useState hook in the parent component and pass it as a prop to the child component. In the parent component, I try to render the display state in a div. However, when the method is triggered (on click of the "drum pad" div), the app crashes. In the console I get an error that says "Uncaught Invariant Violation: Objects are not valid as a React child (found: object with keys {display}). If you meant to render a collection of children, use an array instead."
I've been following along a YouTube tutorial for this project but using arrow function components and Hooks instead of regular classes as used in the tutorial--in the tutorial (around 1:55 of this video) the person successfully does what I'm trying to do, so I think the issue is something to do with using Hooks or arrow function components.
// APP COMPONENT (PARENT)
const sounds = [
{ id: 'snare', letter: 'Q', src: 'https://www.myinstants.com/media/sounds/snare.mp3' },
// etc.
];
const App = () => {
const [display, setDisplay] = useState(''); // <----
const handleDisplay = display => { // <----
setDisplay({ display });
}
return (
<div className="App">
<div className="drum-machine">
<div className="display">
<p>{display}</p> // <---- Related to error in console
</div>
<div className="drum-pads">
{sounds.map(sound => (
<DrumPad
id={sound.id}
letter={sound.letter}
src={sound.src}
handleDisplay={handleDisplay} // <----
/>
))}
</div>
</div>
</div>
);
}
// DRUMPAD COMPONENT (CHILD)
const DrumPad = ({ id, letter, src, handleDisplay }) => {
let audio = React.createRef();
const handleClick = () => {
audio.current.play();
audio.current.currentTime = 0;
handleDisplay(id); // <----
}
return (
<div
className="drum-pad"
id={id}
onClick={handleClick}
>
<p className="letter">{letter}</p>
<audio
ref={audio}
id={letter}
src={src}
>
</audio>
</div>
);
}
You're setting the state as an object instead of a string. Remove the curly brackets around it.
const handleDisplay = display => {
setDisplay(display);
}
This was already answered, but since you are following a tutorial, I am assuming you are learning React and wanted to point a couple of things to help you :)
The incorrect use of state was pointed out, but just for clarification (and the reason I think you were using an object): in the "old" way, with Class components, the state used to be an object, and you needed to update it like an object. This example here shows that. With Hooks, you don't need to set the whole State object, only that specific state property. More info here.
Another point is, in your CodePen example at least, you were missing the import for useState. You either need to import it like this import { useState } from React or use it like this React.useState, since this is a separate module, not imported by default when you import React.
The last point is, when creating components using a loop (like your <DrumPad> with the map) you need to provide a "key" attribute. that will help React keep track of things that needs to be updated or rerendered.
O updated your code with those changes in this link, if you wanna see it working:
https://codesandbox.io/s/reverent-browser-zkum2
Good luck and hope you are enjoying React Hooks :)
I have some code similar to below where i am importing a component dynamically and then i want to set some props on it.
I dont have a <component></component> html tag as i am passing the component to a 3rd party plugin that will display the component inside their own component.
the below code works but then i have started seeing the following vue warning error in the console:
option "propsData" can only be used during instance creation with the `new` keyword.
structure of settings variable:
const settings = {
props:{
title: "new title"
}
}
component loading function:
function Load(compName, settings){
import("#/"+ compName +".vue").then((mod)=>{
let mycomponent = mod.default;
const props = settings.props;
mycomponent.propsData = {...props});
//3rd party package needs this format
let template = function(){
return {template : mycomponent};
}
//not shown - code that passes the template variable to the 3rd party package
});
}
is there another way to get the props into the component - or is it ok to ignore this warning?
How to communicate between two independent components in react js of version 16.x.I have used PubSub library to pass data but it didn't work.Is there a way to make this library working in the latest version of react js or any other method to pass data to a different component.Here is my code Cannot pass data using PubSub.publish in react js
You can create a service in JavaScript code
// services.js
class Shared {
sharedData;
}
const serviceName = new Shared();
export default serviceName;
// in you component
import serviceName from './services.js';
// set data
serviceName.sharedData = {somdata:[]};
// get data
code = serviceName.sharedData;
Best way is to use redux.
In React, you can pass data (state) from parent to child. You can also pass callbacks from parent to child, so that an event in the child will trigger an event in the parent that will modify the data (state) of the parent.
I am trying to develop a web app using react and i have a issue.
my component get a 'exists component name' and I try to render this new component inside render function of the current component.
my current component render function
render(){
let Xxx = null;
if( this.props.onHex ){
console.log( this.props.onHex );
Xxx = <this.props.onHex />
}
return(
<div className="myClass">
<div className="anotherClass">
{Xxx}
</div>
</div>
);
}
}
it not works for me, the console log returns the name of the new component "Unit". when I replace the Xxx = <this.props.onHex /> with this Xxx = <Unit /> it works and render the Unit's render function.
it looks like react not recognise <Unit/> as component.
what I am doing wrong please advise.
my Unit code:
export default class Unit extends Component{
render(){
return(
<div>test</div>
);
}
}
UPDATE:
when I use const XxxName = Unit; Xxx = <XxxName />; it works for me but I want to be able to render the component from string ( I got this string from json ).
I guess I can create all my possible components at this situation inside a file load them into array or something and get them by string, but it's not something that can live with I have a lot of components maybe if I will some how load them from separate folder ( individual file for each component ) it will be half solution. but I still looking how to load component from string.
jsFiddle with another similar issue http://jsfiddle.net/dhjxu5oL/
UPDATE 2:
I am not found elegant way to reach my goal (I don't sure if it exists) for now I am using method for each dynamic component for hope that someone will advise me with more elegant solution. check it: React / JSX Dynamic Component Name
newExampleComponent() {
return <ExampleComponent />;
}
newComponent(type) {
return this["new" + type + "Component"]();
}
let Xxx = null;
if( this.props.onHex ){
const XxxName = this.props.onHex;
Xxx = <XxxName />;
}
Check this jsfiddle for example
UPDATE:
According to React official docs
You cannot use a general expression as the React element type. If you
do want to use a general expression to indicate the type of the
element, just assign it to a capitalized variable first. This often
comes up when you want to render a different component based on a
prop:
So you need to assign this.props.onHex to a CAPITALIZED variable first then you should be able to use it.
UPDATE again
Seems you want to pass a string, not a reference to the component. There is a dirty way to do that
const xxx = this.props.onHex || "";
const XxxComp = eval(xxx);
...
return (<XxxComp />);
I created this codepen for testing
In our project we are using React and Web Components to develop reusable UI components (which in turn will be used by various dev teams internally). Components are developed in React and registered as custom HTML elements through Web Components. We need a way through which we can define the props in HTML custom tag and access all the props in our React Component.
The HTML would be like
<custom-element props1='pageInfo' props2='mediaInfo'></custom-element>
pageInfo and mediaInfo will be JS Objects which will be declared in global window scope or they can be inside some other NameSpace/Object, in that case the HTML would be something like
<custom-element props1='NS.pageInfo' props2='NS.mediaInfo'></custom-element>
OR
<custom-element props1='NS.Page.pageInfo' props2='NS.Media.mediaInfo'></custom-element>
So, we need a way to get all the props defined in the HTML and resolve them as Objects and pass it on to ReactDOM.render
Currently the code to render and register custom element is,
class RegComponent extends HTMLElement {
constructor() {
super();
}
createdCallback() {
ReactDOM.render(<App props1={eval(this.getAttributes('props1'))}/>, this);
}
}
document.registerElement('custom-element', RegComponent);
We want to get rid of eval and all the declared props should be fetched from HTML and passed on to ReactDOM.render. Looking for something like,
ReactDOM.render(<App {getAllProps()}/>, this);
where getAllProps() should return all the props name & their value. Remember that I'm using ES6. Any help would be appreciated!
What about instead of using JSX:
ReactDOM.render(<App props1={eval(this.getAttributes('props1'))}/>, this);
Use React directly, with an adapter transforming attributes into props:
ReactDOM.render(React.createElement(App, {...getAllProps(this.attributes)}), this);
function getAllProps(attributes) {
var props = {};
for (var i = 0; i < attributes.length; i++) {
props[attributes[i].nodeName] = attributes[i].nodeValue;
}
return props;
}
If getAllProps() returns an object, and each property in the object is the prop you wanted, you should only need to update you render to use the spread operator(...). This will deconstruct your object so that each property is passed to the App as a prop.
Here is what it would look like:
ReactDOM.render(<App {...getAllProps()}/>, this);