I have use react/redux with rails project. So i want my Listing Component to be pretender(server sider render) and Other component just show the detail when mouse over on listing item.
Mouse Hover event
My Question is How can i get listing data on Detail Component when mouse over on each listing item
Simple Example
My Code on rails view
= react_component('Listing', { data: #listings }, prerender: true )
= react_component('Detail', { }, prerender: false )
My Code on JS
export default class Listings extends Component {
render() {
return (
<Provider store={store}>
<ListingsWidget />
</Provider>
);
}
}
My Code for Detail
export default class ListingDetail extends Component {
render() {
return (
<Provider store={store}>
< ListingDetail Widget />
</Provider>
);
}
}
You have some pseudo code there, but you'll have 3 components: Listings, ListingsItem, and ListingsItemDetail. You'll have a React onMouseOver attribute on an element in your ListingsItem that will call your event handler to set state. Assuming your ListingsItemDetail component is within ListingsItem, you'll check state to see if you should show ListingsItemDetail. If ListingsItemDetail is somewhere else, then you'll either call an event handler passed in as a prop or use Redux or something to set the id for the ListingsItemDetail that should be displayed.
Edit - added a partial example:
const ListItem = React.createClass({
getInitialState() {
return {showDescription: false}
},
handleMouseOver() {
this.setState({showDescription: true})
},
handleMouseOut() {
this.setState({showDescription: false})
},
renderDescription() {
if (this.state.showDescription) {
return (
<ListItemDescription description={this.props.item.description} />
)
}
},
render() {
return (
<div onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}>
List item title: {this.props.item.title}
{this.renderDescription}
</div>
)
}
})
Related
A client request a feature to implement dashboard switching. I'm working on it:
Dashboard.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
// components
import UserDashboard from '../components/dashboard/user-dashboard/UserDashboard.js';
import NewUserDashboard from '../components/new-dashboard/user-dashboard/NewUserDashboard.js';
#connect((state) => {
return {
identity: state.identity.toJS().profile
};
})
export default class Dashboard extends Component {
render() {
const msisdn = this.props.location.state ? this.props.location.state.msisdn : null;
return (
<UserDashboard msisdn={ msisdn }/>
);
}
}
Dashboard.js is the dashboard controller. I have 2 dashboards: UserDashboard, and NewDashboard.
Let's say an user is viewing another screen, and in that screen there's a button. If that button is clicked, the Dashboard will call it's render method, returning NewDashboard instead. And NewDashboard will be automatically displayed. Is this possible?
Calling render method programmatically not possible.
You have to do state update of that particular component if you want to call render method of that component.
Say,if you want to call render method of Dashboard Component,you must call setState on this component. You can do some dummy state lifting for that.
Imagine you have this dashboard:
function DashBoard({index}) {
return index == 0 ? <UserDashBoard /> : <SecondDashBoard />;
}
Without a router:
class ParentComponent extends ReactComponent {
state = {
dashboardIndex: 0
}
changeDashboard() {
this.setState({
dashBoardIndex: (state.dashboardIndex + 1) % 2
})
}
render() {
return (
<div>
<button onclick={() => this.changeDashboard()}>Change dashboard</button>
<Dashboard index={this.state.dashboardIndex} />
</div>
)
}
}
With a router:
<Switch>
<Route match="/component1" component={UserDashboard} />
<Route match="/component2" component={SecondDashboard} />
</Switch>
Also you can use redux.
You can use conditional rendering using state.
You can keep track of currently active tab and use that state to render the desired component.
More often than not, in order to change page views, you would make use of Router. You can configure Routes corresponding to Dashboard
import UserDashboard from '../components/dashboard/user-dashboard/UserDashboard.js';
import NewUserDashboard from '../components/new-dashboard/user-dashboard/NewUserDashboard.js';
#connect((state) => {
return {
identity: state.identity.toJS().profile
};
})
export default class Dashboard extends Component {
render() {
const msisdn = this.props.location.state ? this.props.location.state.msisdn : null;
return (
<BrowserRouter>
<Route path="/dashboard/user" render={(props) => <UserDashboard msisdn={ msisdn } {...props}/>} />
<Route path="/dashboard/new" render={(props) => <NewUserDashboard msisdn={ msisdn } {...props}/>} />
</BrowserRouter>
);
}
}
and on button click you can use a link.
Or else you can conditionally render component based on state change
// components
import UserDashboard from '../components/dashboard/user-dashboard/UserDashboard.js';
import NewUserDashboard from '../components/new-dashboard/user-dashboard/NewUserDashboard.js';
#connect((state) => {
return {
identity: state.identity.toJS().profile
};
})
export default class Dashboard extends Component {
state = {
userDashboard: true
}
onToggle=(state)=> {
this.setState(prevState => ({
userDashboard: !prevState.userDashboard
}))
}
render() {
const msisdn = this.props.location.state ? this.props.location.state.msisdn : null;
return <div>{userDashboard? <UserDashboard msisdn={ msisdn }/>
: <NewUserDashboard msisdn={ msisdn }/>}
<button onClick={this.onToggle}>Toggle</button>
</div>
);
}
}
Probably something like:
class NewDashboard extends React.Component {
static triggerRender() {
this.forceUpdate();
}
// or
static altTriggerRender() {
this.setState({ state: this.state });
}
render() {...}
}
Force React Component Render
Though, it's better to show/hide other components by conditional rendering.
Update:
"This" is not accessible inside a static method. Ignore the code.
I am using the react-speech-recognition package to do speech-to-text in my app.
Inside render of app.js:
<ChatContainer
micActive={this.state.micActive}
sendData={this.sendData}
makeInactive={this.makeInactive}
>
<SpeechToText>
sendData={this.sendData}
makeInactive={this.makeInactive}
micActive={this.state.micActive}
</SpeechToText>
<div>
<button
id="micInactive"
type="button"
onClick={this.makeActive}
/>
</div>
</ChatContainer>
As you can see above, my ChatContainer has two Children :
SpeechToText
div that contains a button
SpeechToText.js :
class SpeechToText extends Component {
componentWillReceiveProps(nextProps) {
if (nextProps.finalTranscript && nextProps.micActive) {
this.props.sendData(nextProps.finalTranscript);
this.props.resetTranscript();
this.props.makeInactive();
}
}
render() {
return (
<div>
<button
id="micActive"
type="button"
/>
</div>
);
}
}
export default SpeechRecognition(SpeechToText);
SpeechToText receives the speech recognition props from Speech Recognition
ChatContainer.js
const ChatContainer = props => (
<div>
{
React.Children.map(props.children, (child, i) => {
if (i === 0 && child.props.active) {
return React.cloneElement(child, {
sendData: props.sendData,
makeInactive: props.makeInactive,
micActive: props.micActive,
});
}
if (i === 1 && child.props.inactive) {
return child;
}
return null;
})
}
</div>
);
export default connect()(ChatContainer);
Finally ChatContainer decides which child to render. If the state is inactive render the div with the inactive button.
EDIT
By default the state in inactive -- this.state.micActive: false. If the state is inactive I render the <div> with the button. If that button is clicked the makeActive method gets called and makes the state active -- if the state is active I render <SpeechToText>. Once I complete the voice-to-text I call makeInactive -- that makes the state inactive and the <div> is rendered once again
The first time I click the button SpeechToText gets rendered and voice-to-text works.
However the second time I click the button -- And I try to rerender the SpeechToText component I get an error:
setstate can only update a mounted or mounting component
Sometimes the error does not appear but the voice-to-text does not work.
Why is this happening - Do I need to force remove the component perhaps?
Turns out it was an issue with the SpeechRecognitionContainer.
The package was updated with new props and configuration options and I resolved my issue.
You can read more about react-speech-recognition here.
Now simply I can render the component like so:
render() {
return (
<SpeechToText
sendSpeechToText={this.sendSpeechToText}
/>
);
}
and SpeechToText looks something likes this:
class SpeechToText extends Component {
constructor(props) {
super(props);
this.reactivate = this.reactivate.bind(this);
}
componentWillReceiveProps(nextProps) {
if (nextProps.finalTranscript && nextProps.micActive) {
this.props.sendSpeechToText(nextProps.finalTranscript);
this.props.resetTranscript();
this.props.stopListening();
}
}
componentWillUnmount() {
if (this.props.listening) {
this.props.abortListening();
}
}
reactivate() {
if (!this.props.listening) {
this.props.startListening();
}
render() {
return (
<div>
<button
id="micButton"
type="button"
onClick={this.reactivate}
/>
</div>
);
}
}
const options = {
autoStart: false
}
export default SpeechRecognition(options)(SpeechToText)
There is a main component, which uses a menu component. The menu component is using a state property to save the information about selected menu item. But now I need to get the selected module in the main component. How do I do that?
class Main extends Component {
doSomething(module) {
console.log(module) // should get 'targetValue'
// I need to get the info, which module is selected.
// This info is stored as a state value in the `MainMenu` Component
// How do I get this information? I can't use the parameter `selectModule` as it is done here.
}
render() {
return (
<div>
<MainMenu />
<Button
onClick={ this.doSomething.bind(this, selectedModule) }
/>
</div>
)
}
}
In this component a menu is generated for each module (of modules array). By clicking on one item, this module is stored into module state variable.
class MainMenu extends Component {
constructor(props) {
super(props)
this.state = {
module: 'initialValue'
}
}
selectModule(module) {
this.setState({ module })
}
render() {
return (
<Menu>
<Menu.Item onClick={ this.selectModule.bind(this, 'targetValue') } >
{ title }
</Menu.Item>
</Menu>
)
}
}
Instead of doing some magic and examining internal state if children components lift the state to parent. Child becomes stateless.
class Main extends Component {
state = {
module: 'initialValue'
}
setActiveModule = (module) => {
this.setState({ module })
}
render() {
return (
<MainMenu onChange={this.setActiveModule} />
)
}
}
class MainMenu extends Component {
onClick = (module) => () => {
this.props.onChange(module)
}
render() {
return (
<Menu>
<Menu.Item onClick={this.onClick(title)} >
{title}
</Menu.Item>
</Menu>
)
}
}
Instead on maintaining the state in MainMenu component, maintain in parent component Main, and pass the module value in props, also pass a function to MainMenu to update the state of parent component Main from child MainMenu.
Write it like this:
class Main extends Component {
constructor(props) {
super(props)
this.state = {
module: 'initialValue'
}
this.update = this.update.bind(this);
}
update(value){
this.setState({
module: value
});
}
doSomething(){
console.log(this.state.module);
}
render() {
return (
<div>
<MainMenu module={this.state.module} update={this.update}/>
<Button
onClick={ this.doSomething.bind(this) }
/>
</div>
)
}
}
class MainMenu extends Component {
selectModule(module) {
this.props.update(module);
}
render() {
console.log(this.props.module);
return (
<Menu>
<Menu.Item onClick={this.selectModule.bind(this, 'targetValue') } >
{ title }
</Menu.Item>
</Menu>
)
}
}
Sharing state with react is sometimes a bit hard.
The react philosophy tends to say that we have to manage state from top to bottom. The idea is to modify the state in your parent, and pass the informations as props. For example, let's imagine the following scenario :
class Main extends React.Component {
contructor(props) {
super(props);
this.state = { currentMenuSelected: 'Home' };
}
onPageChange(newPage) {
this.setState({ currentMenuSelected: newPage });
}
render() {
return(
<div>
<AnotherComponent currentMenu={this.state.currentMenuSelected} />
<MenuWrapper onMenuPress={this.onPageChange} />
</div>
)
}
}
In my example, we tell the MenuWrapper to use the Main.onPageChange when changing page. This way, we're now able to pass that current selected menu to AnotherComponent using props.
This is the first way to manage state sharing using react, and the default one provided by the library
If you want to manage more complex stuff, sharing more state, you should take a look at the flux architecture https://facebook.github.io/flux/docs/overview.html
and the most common implementation of flux : http://redux.js.org/
Store the menu state in the main component, and pass the state updater down to the menu.
This is quite helpful in getting into top-down state
https://facebook.github.io/react/docs/thinking-in-react.html
Using Meteor 1.3 and React, I've made the following main component:
//imports
export default class Show extends TrackerReact(Component) {
renderOptions() {
return this.options().map((option) => (
<Option key={ option._id } option={ option }/>
));
}
options() {
return Options.find({}).fetch();
}
render() {
return (
<div className="container">
<ul>
{ this.renderOptions() }
</ul>
</div>
);
}
}
And I have the following Option component:
export default class Option extends Component {
constructor(props) {
super(props);
this.state = {
disabled: false
};
}
vote() {
this.setState({ disabled: true });
}
renderButton() {
return this.state.disabled ? 'btn btn-success' : 'btn btn-default';
}
render() {
return (
<li>
<button disabled={ this.state.disabled ? 'disabled' : '' } type="button" className={this.renderButton()} onClick={this.vote.bind(this)}>
Vote
</button>
<span> { this.props.option.text }</span>
</li>
);
}
}
Option.PropTypes = {
option: PropTypes.object.isRequired
};
How can I make it so when I click the button (which invokes the component's vote() method) on any one of the options rendered by the Show component the disabled state of each button is set to true?
Basically, I am rendering out a few buttons for the user to click, and once he clicks one, I want to disable all of the others. Therefore, I need to set the state of all buttons to disabled when one is clicked.
this is more of a React question than a Meteor question, but what you would need to do is to declare your function in your parent component and then pass it down as a prop to your child component. Then inside your child component you can call that function with this.props.funcName.
So in your example you'd want to declare the state in your Show component (or higher if it affects other components) and then pass down a function that updates the state in your parent component. This state would then be passed down as props to your Option component.
Let me know if that isn't clear
I'm working on a RN app and I'm running into an issue where I try to change the state of a component due to an event that happens in a child component and I'm getting inconsistent results.
var app = React.createClass({
render: function() {
return (
<MainLogin onLogin={this.onLogin} />
);
},
onLogin: function() {
this.setState({isLoggedIn: true});
}
});
class MainLogin extends Component {
render() {
return(
<Login changeSomething={this.changeSomething} onLogin={this.props.onLogin}/>
);
}
changeSomething() {
this.setState({something: true});
}
}
class Login extends Component {
constructor(props) {
super(props);
}
loginPressed() {
this.props.onLogin(); //This works
}
changeSomething() {
this.props.changeSomething(); //This doesn't work
}
render() {
return (
<View style={{flex: 1}}>
<Button onPress={this.changeSomething.bind(this)}>Change Something</Button>
<Button onPress={this.loginPressed.bind(this)}>Login</Button>
</View>
);
}
}
The onLogin function runs perfectly and is able to set the state of the Grandparent component, whereas the changeSomething function fails (this.setState is not a function).
Any suggestions?
You should bind component methods in ES6 Classes.React.createClass automatically doing that, but your second and third components are subclassed with ES6 classes. Here is an explanation about that in the official website.