I have built a golden layout wrapper using golden-layout-react-redux, and I'm creating new layouts and connecting them to the panels reducer so every time I add a new panel it will get built inside the GoldenLayoutWraper. I connected the config content like this:
componentDidMount() {
const config = {
content: [{
type: "column",
content: this.props.panels//here
}]
};
goldenlayout = new GoldenLayout(config, this.layout);
goldenlayout.registerComponent(
"IncrementButtonContainer",
wrapComponent(IncrementButtonContainer, this.context.store)
);
}
goldenlayout.init();
But I'm facing a problem that it is not updating when I add new panel to the reducer, the panels reducer array increases but the contentIems are not.
So I was forced to do it like this:
render() {
this.props.panels.map(panel =>
setTimeout(() => {
goldenlayout.root.contentItems[0].addChild(panel);
}, 100)
);
return (
<div className="goldenLayout" ref={input => (this.layout = input)} />
);
}
this code is inside the GoldenLayoutWrapper component render(), and the problem here is that:
1- Every time I add new panel it adds multiple panels, because it is adding panels.length plus the already existed items in contentItems[0].
2- when I delete all the panels I can not add anymore because the way golden-layout works that when there is only one child it replaces it and become the root child not the contentItems[0] so this goldenlayout.root.contentItems[0].addChild(panel) throws an error.
I also tried to use this react-golden-layout instead but I'm stuck with this issue
P.S: I'm a beginner
Related
In my mongoDB I have documents with nested objects that corresponds to which make, model and year of the motorbike they fit to.
Example:
fits: {
honda: {
crf250: {
1990: true,
1991: true
},
rx400: {
2000: true
}
},
kawasaki: {
ninja: {
2015: true
}
}
}
I need to loop through all the makes that the document stores in fits field (In the example above it would be honda and kawasaki) and than return all the models that exist under the specific make. I am succesfully receiving the array of all the models under the make in my aggregate method.
return(
<ul style={{listStyleType: 'none'}}>
{Object.keys(props.data.fits).map((make, i) => {
if(db !== null && client !== null){
var query = `fits.${make}`;
var pipeline = [
{
$match: {
[query]: {
'$exists': true,
'$ne': {}
}
},
},
{
$group: {
_id: `$${query}`,
}
}
]
client.auth.loginWithCredential(new AnonymousCredential()).then((user) => {
db.collection('products').aggregate(pipeline).toArray().then((models)=>{
return <Make
style={{border: '1px solid grey'}}
mongoClient={props.mongoClient}
id={props.data._id}
key={i}
make={make}
data={props.data}
_handleDeleteMake={handleDeleteMake}
_updateRowData={props._updateRowData}
_models={models}
>
</Make>
}).catch(e=>console.log(e))
})
}
})}
</ul>
)
However after the call I need to render the makes. It should look something like this:
Next to the orange plus I want to show the list of all the other models that exists under the specific make so I don't have to repeat in writing the model again if its exists already and I can just click on it.
However rendering Make inside the async I am left with blank:
Now from what I understand is that the render finished before the async function finished that is why it simply renders empty list, but I don't really know how should I approach this problem. Any suggestions?
I don't think it's possible for you to render a React element in that async way. When React try to render your element that is inside the ul tags, because you are using async, at the time of DOM painting, there is nothing for React to render. Thus React render blank.
After the async is resolved, React won't re-render because React doesn't know that there is a new element being added in. Thus even when you actually have that element, since React doesn't re-render, you won't see that element in the app
Why does this happen? Because React only re-render when there are certain "signal" that tells React to re-render. Such is state change, props change, hooks call, etc. What you did doesn't fall into any of those categories, so React won't re-render. This is the same reason why you don't directly change the component state and instead must use method like setState to change it.
In my NextJS application interfaced with a MongoDB API back-end - managed via GraphQL, I'm trying to implement the Apollo fetchMore feature, in order to update a component responsible to load more items from a data collection.
On page rendering, the component itself shows a "gallery" of 10 elements as its native functionality, populated via a GraphQL starting query. Then, I included a "load more" button to trigger the fetchMore function. The UX expects that if the user clicks the proper button, more 10 elements will going to be loaded in addition of the previous 10 - basically a classical async-infinite loading example.
By inspecting the app, I notice that both the queries are being returned successfully - the initialization one and the "load more 10 items" too managed by fetchMore - but the latter, after its execution, triggers the component's update that it's being re-initialized with the starter query instead of the fetchMore one.
To clarify it: on "load more" click, instead to see the next 10 gallery elements loaded - so to finally display a total of 20 - the component refreshes and displays the starter 10 elements, like its starting initialization - totally ignoring the fetchMore action, even if this one is being called, executed and received back with a populated 200 response.
Because this is my very first time in using it, I don't know if I'm missing something in my implementation, or I need to fix something. Anyway, here it goes:
Due to various reasons, I'm running the query in a parent component, then I pass the data as props to a child one:
Parent
// Initialization, etc.
[...]
const {loading: loadingIndex, error: errorIndex, data: dataIndex, fetchMore: fetchMoreIndex} = useQuery(ARTICLE_QUERY.articles.indexArticles, {
// Last 30 days
variables: {
live: live,
limit: 10
}
});
// Exception check
if (errorIndex) {
return <ErrorDb error={errorIndex} />
}
// DB fetching check
if (loadingIndex) {
return (
<section className="index-articles">
<h6>Index - Articles</h6>
<aside className="articles__loading">
<h6>Loading</h6>
</aside>
</section>
);
}
const articles = dataIndex.queryArticleContents;
return (
<IndexArticles labels={props.labels} articles={articles} fetchMore={fetchMoreIndex} />
);
Child
// Initialization, etc.
[...]
let limit = 10; // My query hypothetically limiter
const IndexArticles = (props) => {
useEffect(() => {
// This is a getter method responsible to manage the ```fetchMore``` response
getArticles(props.articles, props.fetchMore);
});
return (
<>
// Component sections
[...]
// Load button
{props.fetchMore &&
<button className="articles__load" title={props.labels.index.title} tabIndex={40}>{props.labels.index.cta}</button>
}
</>
);
function getArticles(articles, fetchMore) {
// Yes, I'm using jQuery with React. Just ignore it
$('.articles__load').on('click tap', function(e) {
e.preventDefault();
$(this).addClass('hidden');
$('.articles__loading').removeClass('hidden');
fetchMore({
variables: {
// Cursor is being pointed to the last available element of the current collection
lastLoaded: articles.length,
limit: limit += 10
},
updateQuery: (prev, {fetchMoreResult, ...rest}) => {
$('.articles__loading').addClass('hidden');
$(this).removeClass('hidden');
if (!fetchMoreResult) {
return prev;
}
return {
...fetchMoreResult,
queryArticleContents: [
...prev.queryArticleContents,
...fetchMoreResult.queryArticleContents
]
}
}
});
});
}
Anyone have experience with it or had experienced this case before?
Thanks in advance for the help
As suggested on the official community, my configuration was missing about the notifyOnNetworkStatusChange: true in the query options, which is responsible to update the component and append the new data.
By changing the code in this way:
Parent
const {
loading: loadingIndex,
error: errorIndex,
data: dataIndex,
// Add networkStatus property too in order to use notifyOnNetworkStatusChange properly
networkStatus: networkStatusIndex
fetchMore: fetchMoreIndex} = useQuery(ARTICLE_QUERY.articles.indexArticles, {
// Last 30 days
variables: {
live: live,
limit: 10
},
// Important for component refreshing with new data
notifyOnNetworkStatusChange: true
});
The problem has been solved.
Can you please advise what is missing in my code (https://codesandbox.io/s/x2q89v613o) that causes copies of components to be created on resize even though I had assigned unique keys to them?
Project is simple scheduler table with each cell being a component and event is also component. Some complexity added by using React Drag and Drop .. could it be that using HOC wrapper makes React do not recognize existing elements?
Thanks!!
VB
Add your componentWillReceiveProps with below in WeekView, I have added a line cell.events= [];. This is clear all previous events and add the new which came in props.
componentWillReceiveProps(nextProps) {
if (nextProps.events && nextProps.events.length) {
console.log("weekView componentWillReceiveProp got events");
this.state.cells.forEach(cell=>{
cell.events= [];
});
nextProps.events.forEach(x => {
const start = x.start;
const cellId = "c" + "_" + moment(start).valueOf();
const target = Helper.getItemFromArray(this.state.cells, cellId, "id");
if (target) {
console.log("found");
target.events.push(x);
}
});
console.log(
this.state.cells.filter(x => {
if (x.events.length > 0) return x;
})
);
}
}
The below line is causing the problem
tableWidth: ReactDOM.findDOMNode(this._tableTarget).offsetWidth
offsetWidth is changing on screen size change and hence creating a copy.
Remove it and try.
tableWidth: "auto"
or
tableWidth: "100%"
I've just started on reactjs and am trying to figure out the right way to design my components. I get the concept of components, props, states but struggling a bit with the right way to design for a hierarchy.
I have a page that's driven by a big object array. Say
objArr = {
param1 : value1,
param2: value2,
dataArray: [
{ key: "keyvalue", a: "a", b: "b", c: "c" },
...
]
}
The entire page builds off of this. The page builds a series of UI components corresponding to the dataArray.
Now each time some of the icons are clicked in the UI, I want some changes, and the icons correspond to a value on this dataArray. What's a good way to ensure the dataArray values are changed as the UI is acted on? and vice versa, the UI changes as values are changed on the dataArray.
I've read "make components as stateless as possible," fine - then how do I do this handling at the parent component level and have it flow down?
I don't need code examples. Just a few pointers of the way to architect my ReactJS code would be great, I will figure out the code.
Thank you
What you can do is bind a method on the Parent component and pass it down to the child container like so:
// Our 'pages' that we want to be able to interact with
const ComponentWIthButtons = ({handleButtonClick, buttons}) => (
<div>
<p>Hey, click one of these</p>
{buttons.map(button => (
<div onClick={()=>handleButtonClick(button.name)}>
{button.text}
</div>
))}
</div>
)
// The Parent we want to interact with
class App extends React.Component {
constructor(props){
super(props)
this.state = {
buttons: [
{
name: 'a',
text: 'Awesome button'
},
{
name: 'b',
text: 'Beautiful button'
},
{
name: 'c',
text: 'Cool button'
}
]
}
}
// This is what you want to do with the information passed
handleButtonClick = (name) => {
// this could be updating this components' state, which
// would rerender the display component because we pass it
// our buttons from our state object
console.log(name)
};
render(){
return <div>
<h2>Below are some buttons!</h2>
// If you don't use the syntax above, you will run into
// errors trying to get a reference to this container through
// `this`. You can do this.handleButtonClick.bind(this)
// to get around that
<ComponentWIthButtons handleButtonClick={this.handleButtonClick} buttons={this.state.buttons} />
</div>
}
}
All:
I am pretty new to React, I am trying to render a component from the string return by Server side ReactDomServer.renderToString(), could anyone give me a working patrn or example to do this in AJAX?
A case will be:
One the init page, there is a dropdown, you choose different type of componnet, then it will submit AJAX request to server, then server return according string, then the page will render that component on it.
Thanks
From the comments on your question, it sounds like what you're looking to do is dynamically render a UI.
Your request was for an example of a drag-and-drop rendering workflow, but that would stray too far from your question. It's important that we first tease out the many components and then focus on the one that's interesting for this question. We have a data layer and server-side responsible for storing information, business logic for determining how components should render where and when, interaction paradigms like drag and drop that work within these rules, and the rendering of components based on them.
All of these are separate concerns that must be considered independently. For example, drag and drop is one way to add components, but it is likely to not be the only way, so why couple the two? That leaves us with just rendering dynamic components, which I shall consider here. I'll be using ES2015 syntax to make the code cleaner.
First, we have a main component that does the wrapping:
const Renderer = React.createClass({
render () {
// ...
},
});
ReactDOM.render(
<Renderer layout={layout} />,
document.getElementById( 'app' )
);
Now let's consider the components you mentioned, which will be pure:
const Button = ({ text }) => (
<button>{text}</button>
);
const Input = ({ type = "text", placeholder }) => (
<input type={type} placeholder={placeholder} />
);
And some container for available components (which would likely also have metadata and rules about each):
const Components = {
Button,
Input,
};
And now let's assume we have a configuration defined in json:
{
"name": "My Interface",
"layout": [
{ "id": 123, "component": "Input", "placeholder": "keywords..." },
{ "id": 456, "component": "Button", "value": "Search!" },
],
}
This is highly simplified, but you can imagine this document showing all properties for a deeply nested UI, perhaps sporting different types of containers like rows and columns. Now we can assume the JSON property layout is the layout property passed to the Renderer above. Now our render function can look like this (highly simplified):
render () {
const children = this.props.layout.map( ({ component, ...props }) => {
const Component = Components[ component ];
return <Component {...props} />
});
return (
<div className="component-view">
{children}
</div>
);
}
Whenever the model changes, we would re-render the component tree and see what we should. There is a lot that would have to go into something like this to get a full UI editor - that's a massive undertaking. But with proper design principles and separation of concerns, it's at least doable.
To return to drag and drop briefly, if we were to drag and drop, we would note its place and insert the component into the tree however made sense based on the component and the state of the item onto which it's dropped, etc. The result of the operation, assuming it was successful, would be a mutated layout tree, which triggers a re-render.
Note: I completely ignored performance considerations.