How to setState to one particular component instance in RefluxJS? - reactjs

I'm currently doing one small project in React + Flux (RefluxJS) and I faced wit one issue I can't solve. I would be very gratefully if someone of you can give me a hand.
Here you have the link to GitHub with the whole project in order to facilitate your help, it's a simplest version just to reproduce the problem I faced.
My doubt is, how can I use one component in the same view with different content. Let me explain:
I have on component in, "components/threads.jsx" which in summary render this peace of code getting the data from the store ("stores/thread-store.jsx") throug a fake API ("utils/api.jsx"):
renderThreads: function() {
return this.state.thread_content.map(function(thread, i) {
return (
<div key={thread.id}>
<div className="row">
<div className="col-md-10">
<a data-toggle="collapse" href={'#thread-' + thread.id} className="faq-question">{thread.name}</a>: <small>{thread.content}</small>
</div>
</div>
<div className="row">
<div className="col-lg-12">
<div id={'thread-' + thread.id} className="panel-collapse collapse ">
<Posts id={thread.id} />
</div>
</div>
</div>
</div>
);
});
},
As yo can see, I have another component nested called "Posts" in "components/thread-posts.jsx" which is rendered for each thread is mapped. In the "Posts" component I have this peace of code:
module.exports = React.createClass({
mixins: [
Reflux.listenTo(PostsStore, 'onChange'),
],
getInitialState: function() {
return {
posts: []
}
},
componentDidMount: function() {
Actions.getPosts(this.props.id);
},
render: function() {
return (
<div>
{this.renderPosts()}
</div>
);
},
renderPosts: function() {
if(this.state.posts.comments != undefined){
return this.state.posts.comments.map(function(post, i) {
return(
<div key={i} className="faq-answer">
<p>
{post.content}
</p>
</div>
);
});
}
},
onChange: function(event, posts) {
this.setState({
posts: posts,
});
}
Here comes the problem. When finish the render, all the threads have the same posts, in particular the lasts ones were set. I think is related with the states, if they change it will be change in all the components were rendered.
So, my question is, how can I deal with it in order to have the posts according to its thread but using the same component? If it's not posible, which is the best solution to do that?
I hope explained myself as well as enough to understand me.
I will be very gratefully if you can give me a hand in this issue.
Thanks in advance.

Yes if you share the Store with your View then latest one will be overwrite your previous Component's data
you have to create a separate variable which can hold the data of each API Call and when call API you have to pass the Key like
let's take one example
I have one Component it's called MainComponent
I want to use same Component on same page twice but the data should be different for both component
I have one Store for MainComponent called MainStore
In MainStore I have one Method called getData like
getData:function(key)
{
//API Call
}
now I am calling this method from my MainComponent from componentDidMount event like
componentDidMount:function()
{
//if you used Action then you have to called like
MainComponentAction.getData(this.props.Key);
// if you directly called
MainComponentStore.getData(this.props.Key);
}
here this.props.Key you have to pass from the parent component which should 1 or 2
now come to store we have passed the Key so now we have to check condition while we received a data from API
suppose I have taken one variable in which I am storing the data which is return by API like
now you have to create two methods for store the data based on key
var _data,_data1
function loaddata(APIdata,key)
{
if(key===1)
{
_data=APIdata
}
else
{
_data1=APIdata
}
}
and in your store you to methods for getting the data like
getData:function()
{
return _data;
},
getData1:function()
{
return _data1;
}
now your getintialState of MainComponent Should be
getintialState:function()
{
return{
Data:JSON.Parse(MainComponentStore.getData()),
Data1:JSON.Parse(MainComponentStore.getData1()),
}
}
and your MainComponent render function should be
render:function(){
var LatestData;
if(this.props.Key===1)
{
LatestData=this.state.Data
}
else if(this.props.Key===2)
{
LatestData=this.state.Data1
}
return(
<div>
{LatestData}
</div>
)
}

Related

disable react css transition group on certain events

I currently have a dashboard type app that pulls messages out of multiple slack channels and displays the ones without any replies or emojis. You can change which channel you want to view.
Currently, the parent state looks something like this:
state = {
selected_channel: window.localStorage.getItem( 'last-slack-ch' ) || 'ABC123'
selected_date: new Date(),
channels: {},
items: [],
slack_users: {},
settings: {
seconds_per_slack_messages_pull: 1
},
atBottom: false
}
After pulling in the messages... or items, I pass that array into a component ItemsContainer.
This is the render method in ItemsContainer
render() {
return (
<div className="items-container">
{
this.props.items.length > 0 ?
<ReactCSSTransitionGroup
transitionName='item'
transitionEnterTimeout={500}
transitionLeaveTimeout={500}>
{
this.props.items.map( t => {
return (
<Item
key={ t.usr + '_' + t.ts }
task={ t }
user={ this.props.slack_users[ t.usr ] }
settings={ this.props.settings }
/>
)
} )
}
</ReactCSSTransitionGroup>
:
<p className='nothing-found'>No items were found in channel: { this.props.selected_channel }</p>
}
</div>
);
}
The issue I am currently facing is that there are certain events that I don't want to have a transition. Stuff like: Initial page load and switching channels.
I tried passing in some props to ItemsContainer which will determine what transitionName to the ReactCSSTransitionGroup... but things didnt really work too well.
Anyone have experience with this?
It's hard to tell from your code exactly how you would implement it, but you could add a key property to the top-level div in your ItemsContainer component. The key property is usually used to identify children in collections but can be used outside of those cases. When a key changes, React creates a new component instance rather than re-render the existing (https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a-key).
So try re-writing ItemsContainer as:
render () {
return (
<div className="items-container" key={this.props.selected_channel}>
// Transition code that will only apply within the selected channel.
</div>
);
}
This should solve the switching-channel problem. So when ItemsContainer receives a new props.selected_channel value you should avoid the transition animations.

How to find and modify DOM nodes in the React before render?

i cound't see right way how to find and modify React DOM nodes before render.
For example i need to make all founded images inside component as lazy load. But the problem is that I don't know which nodes will be inserted by the user. Therefore, I need to look for them after the user places nodes in the component, by using componentWillMount or constructor i think...
Actually in jQuery, to replace real src after user insereted it in. I will do something like this:
jQuery('.wrapper img').each(element => {
let realSrc = element.attr('src')
element
.attr('data-real-src', realSrc)
.attr('src', './fakeimage.png')
})
So in the React, i have to do something like this i think...
const modifiedChildren = children.reduce((arr, next) => {
let modified = {
...next,
src: './fakeimage.png',
'data-real-src': next.src
}
return [
...arr,
modified
]
}, [])
render {
<div className="wrapper">{ modifiedChildren }</div>
}
Can you navigate me some please?
Stop thinking in DOM nodes. Start thinking in components.
You want a new component LazyLoadedImage which gets the real URL and displays it after some trigger. Something like that:
import fakeImage from 'assets/fakeimage.png';
const LazyLoadedImage = ({url, displayActualImage, ...props}) => {
if(displayActualImage) return <img {...props} src={url}>;
else return <img {...props} src={fakeImage}>;
}
Usage example:
<Slider>
{images.map((obj, index) =>
<LazyLoadedImage className='slider__img' url={obj.largeImageURL}
alt={obj.tags || 'cap'} key={index}/>)}
</Slider>

Passing children from XHP to ReactJS

I have an XHP component:
final class :common:message-stripe extends :ui:base {
use XHPReact;
protected function compose() {
$this->constructReactInstance( "MessageStripe", Map {} );
return <div id={$this->getID()} />;
}
}
that should look like this in my .php file:
<common:messagestripe>
This is my message :)
</common:messagestripe>
On the ReactJS side, the component looks something like this:
var MessageStripe = React.createClass({
render: function() {
return (
<div className="message"> {this.props.children} </div>
);
}
});
However, I get errors about rendering null in my ReactJS component, which means that children are not sent correctly. So, my question: how can I pass the children from XHP to ReactJS?
You're not including your children when rendering your XHP component.
final class :common:message-stripe extends :ui:base {
use XHPReact;
protected function compose() {
$this->constructReactInstance( "MessageStripe", Map {} );
return <div>{$this->getChildren()}</div>;
}
}
Additionally, (assuming you built your :ui:base element with attribute forwarding as outlined here: Building a Good UI Framework with XHP) you don't need to manually set the ID of your root element, XHPJS::element() will do that for you.

Is this good practice store state in variable?

I have just started to study ReactJS and have some questions. I was reading documentation here, but I can't find the answer I am looking for. Here is an example:
var Awesome = React.createClass({
getInitialState:function() {
return {
txt : ["1","2","3","4","5"],
isTrue : true
}
},
handleClick:function() {
this.setState({
isTrue : !this.state.isTrue
})
},
render:function() {
var changeStyle = {
display: this.state.isTrue ? "block" : "none"
};
var message = this.state.txt.map(function(oneMessage) {
return <SubChild change={changeStyle} txt={oneMessage}/>
});
return (
<div>
<button onClick={this.handleClick} >Click Me</button>
{message}
</div>
)
}
})
var SubChild = React.createClass({
render:function() {
return (
<div style={this.props.change}>
<h3>{this.props.txt}</h3>
</div>
)
}
})
React.render(<Awesome />, document.body)
Everything works fine, but I have some questions. As you can see I store my state inside a variable. Is this the best practice? How can I achieve the same result without variables inside render function or actually without states (I am trying to avoid state). Is this possible?
Here is my Fiddle
Why State Variables?
The idea of using state variables is to have changing / dynamic data, ie if anything about the component is changing, it should be defined as a state variable in the component so user interaction can result in change of this variable and a change in this variable causes the effected component to re-render.
Use of Properties
If some value is changed for each instance of the component and is uneffected by user interaction or component state change, it should be defined as a property so it can be assigned only once at instantiation.
In all cases, we cannot really avoid the use of variables inside the render

ReactJS + Flux setState not working on single click

I'm writing my first reactjs + flux application and I have managed to get the basic code working. However, there is a list, which populates another list depending on which list item is clicked. This was working fine until I had my list data hard-coded in the store. But when I started getting the data for the list items from the server, the second list does not get populated on a single click - it needs two clicks to do that. I don't think that getting data from server is a problem because the default data is populated correctly. After debugging, I found out that the store is also returning the data correctly, but the setState() is not setting the data. What am I doing wrong?
Here's my code:
function getInitialData(){
return{
items:DataStore.getFirstItemData()
}
}
var ListItems = React.createClass({
getInitialState:function(){
return getInitialData();
},
_onChange:function(){
this.setState(getInitialData);
},
componentDidMount:function(){
DataStore.addChangeListener(this._onChange);
},
componentWillReceiveProps:function(){
this.setState({items:this.props.itemsData});
},
handleClick:function(e)
{
var data = DataStore.getSubItemsFor(e);
this.setState({items:data});
},
render:function(){
return(
<div>
<div className="col-md-3 dc-left" id="main-list">
<div className="dc-main-list-parent">
{this.props.listData.map(function(list_items){
return(
<div key={list_items.id} onClick = {this.handleClick.bind(this,list_items.id)}>
</div>
);
},this)}
</div>
</div>
<div className="col-md-9 dc-center" id="sub-list">
<SubList sublistData = {this.state.items} />
</div>
</div>
)
}
});
P.S. This code has been modified a little for posting here. Please overlook any syntactical errors - it works!
Probably things break down because updates to the DataStore trigger 2 changes:
The complete component gets rerendered by the parent component, which fires the componentWillReceiveProps + fires a render cycle.
You also have a listener to changes in the DataStore, so the update in the DataStore also fires the _onChange event, which tries to update the state as well
PS: the code has a typo:
this.setState(getInitialData);
should read:
this.setState(getInitialData());
I would suggest you remove the change listener part, and see if that helps..

Resources