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
Related
i want to show my functional component in class base component but it is not working. i made simpletable component which is function based and it is showing only table with some values but i want to show it when i clicked on Show user button.
import React ,{Component} from 'react';
import Button from '#material-ui/core/Button';
import SimpleTable from "../userList/result/result";
class ShowUser extends Component{
constructor(props) {
super(props);
this.userList = this.userList.bind(this);
}
userList = () => {
//console.log('You just clicked a recipe name.');
<SimpleTable/>
}
render() {
return (
<div>
<Button variant="contained" color="primary" onClick={this.userList} >
Show User List
</Button>
</div>
);
}
}
export default ShowUser;
Why your code is not working
SimpleTable has to be rendered, so you need to place it inside the render method. Anything that needs to be rendered inside your component has to be placed there
On Click can just contain SimpleTable, it should be used to change the value of the state variable that controls if or not your component will be shown. How do you expect this to work, you are not rendering the table.
Below is how your code should look like to accomplish what you want :
import React ,{Component} from 'react';
import Button from '#material-ui/core/Button';
import SimpleTable from "../userList/result/result";
class ShowUser extends Component{
constructor(props) {
super(props);
this.state = { showUserList : false }
this.userList = this.userList.bind(this);
}
showUserList = () => {
this.setState({ showUserList : true });
}
render() {
return (
<div>
<Button variant="contained" color="primary" onClick={this.showUserList} >
Show User List
</Button>
{this.state.showUserList ? <SimpleTable/> : null}
</div>
);
}
}
export default ShowUser;
You can also add a hideUserList method for some other click.
Or even better a toggleUserList
this.setState({ showUserList : !this.state.showUserList});
If you're referring to the method userList then it appears that you're assuming there is an implicit return value. Because you're using curly braces you need to explicitly return from the function meaning:
const explicitReturn = () => { 134 };
explicitReturn(); <-- returns undefined
const implicitReturn = () => (134);
implicitReturn(); <-- returns 134
The problem lies with how you are trying to display the SimpleTable component. You are using it inside the userList function, but this is incorrect. Only use React elements inside the render method.
What you can do instead is use a state, to toggle the display of the component. Like this:
const SimpleTable = () => (
<p>SimpleTable</p>
);
class ShowUser extends React.Component {
constructor(props) {
super(props);
this.state = {showSimpleTable: false};
this.toggle= this.toggle.bind(this);
}
toggle = () => {
this.setState(prev => ({showSimpleTable: !prev.showSimpleTable}));
}
render() {
return (
<div>
<button variant = "contained" color = "primary" onClick={this.toggle}>
Show User List
</button>
{this.state.showSimpleTable && <SimpleTable />}
</div>
);
}
}
ReactDOM.render(<ShowUser />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
The functionality you are looking for is called Conditional Rendering. The onClick prop function is an event handler and events in react may be used to change the state of a component. That state then may be used to render the components. In normal vanilla javascript or jQuery we call a function and modify the actual DOM to manipulate the UI. But React works with a virtual DOM. You can achieve the functionality you are looking for as follows:
class ShowUser extends Component {
constructor(props) {
super(props)
// This state will control whether the simple table renders or not
this.state = {
showTable: false
}
this.userList.bind(this)
}
// Now when this function is called it will set the state showTable to true
// Setting the state in react re-renders the component (calls the render method again)
userList() {
this.setState({ showTable: true })
}
render() {
const { showTable } = this.state
return (
<div>
<Button variant="contained" color="primary" onClick={this.userList}>
Show User List
</Button>
{/* if showTable is true then <SimpleTable /> is rendered if falls nothing is rendered */}
{showTable && <SimpleTable />}
</div>
)
}
}
I have 2 component a parent component to manage the state and a lot of other things and a child component with some reactstrap buttons radio i'm trying to change the state onClick on the child buttons but I get the error: this.setState is not a function and i can't figure out what's wrong with my code =>
//Parent
import React, { Component } from 'react';
import BtnRadio from './btnToggle';
class parent extends Component {
state = {
rSelected: true,
}
onRadioBtnClick(rSelected) {
this.setState({
rSelected:rSelected
});
}
render(){
return (
<div>
<BtnToggle onRadioBtnClick={this.onRadioBtnClick} active={this.state.rSelected}/>
</div>
);
}
};
export default AddAdmin;
//Chlid
import React from 'react';
import { Button, ButtonGroup } from 'reactstrap';
const BtnRadio = (props) => {
return (
<ButtonGroup>
<Button color="light" onClick={() => props.onRadioBtnClick(true)} active={props.active === true}>Enable</Button>
<Button color="light" onClick={() => props.onRadioBtnClick(false)} active={props.active === false}>Disabled</Button>
</ButtonGroup>
);
};
export default BtnRadio;
is there someone who can point me to the right direction i guess that i forgot to bind something...
The problem is, when you're using non-anonymous functions, this gets overridden, and wont refer to the component anymore. Since you're already using class properties, the simple fix, is to keep using the arrow functions, to keep this referencing the component:
onRadioBtnClick = (rSelected) => {
this.setState({
rSelected:rSelected
});
}
See #5 in this medium article, which explains different ways of binding this to keep it referencing the component.
<BtnToggle onRadioBtnClick={() => this.onRadioBtnClick()} active={this.state.rSelected}/>
Arrow function for the rescue.
You should bind the functions your passing like so:
class parent extends Component {
state = {
rSelected: true,
}
onRadioBtnClick(rSelected) {
this.setState({
rSelected:rSelected
});
}
render(){
return (
<div>
<BtnToggle onRadioBtnClick={this.onRadioBtnClick.bind(this)} active={this.state.rSelected}/>
</div>
);
}
}
alternatively, you can bind the functions before passing them in the constructor:
class parent extends Component {
state = {
rSelected: true,
}
constructor() {
super()
this.onRadioBtnClick = this.onRadioBtnClick.bind(this)
}
onRadioBtnClick(rSelected) {
this.setState({
rSelected:rSelected
});
}
render(){
return (
<div>
<BtnToggle onRadioBtnClick={this.onRadioBtnClick} active={this.state.rSelected}/>
</div>
);
}
}
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
I'm trying to build my first React project, and am currently putting together a burger nav button, and a menu which appears when clicking the nav.
I've broken this into two components; Hamburger and MenuOverlay. The code for both is below.
Currently I have an onClick on Hamburger toggling a class on it, but how would I also toggle the menu from that click? It's hidden with display: none; by default. Probably a very basic question so apologies - still trying to get my head around React.
MenuOverlay
import React from 'react';
import { Link } from 'react-router';
const MenuOverlay = () => {
return (
<div className="menuOverlay">
<div className="innerMenu">
<p><Link to="/">Home</Link></p>
<p><Link to="/">About</Link></p>
<p><Link to="/">Contact</Link></p>
</div>
</div>
);
};
export default MenuOverlay;
Hamburger
import React, { Component } from 'react';
class Hamburger extends Component {
constructor(props) {
super(props);
this.state = { active: '' };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
var toggle = this.state.active === 'is-active' ? '' : 'is-active';
this.setState({active: toggle});
}
render() {
return (
<button className={`hamburger hamburger--emphatic fadein one ${this.state.active}`} onClick={this.handleClick} type="button">
<span className="homeMenuTextButton">Menu</span>
<span className="hamburger-box">
<span className="hamburger-inner"></span>
</span>
</button>
);
}
}
export default Hamburger;
In the most simplistic form you would have a container component that wraps around both of them and manages the state of the components.
<MenuContainer>
<Hamburger />
<MenuOverlay />
</MenuContainer>
And in <MenuContainer> you would have a state of active some quick pseudocode.
class MenuContainer extends React.Component {
constructor() {
super();
this.state = { active: false}
}
toggleMenu = () => {
// function that will toggle active/false
this.setState((prevState) => {
active: !prevState.active
});
}
render() {
return (
<div>
<Hamburger active={this.state.active} onClick={this.toggleMenu} />
<MenuOverlay active={this.state.active} />
</div>
)
}
}
so in hamburger you would just use the this.props.onClick to change the state of active and then in those corresponding components use the prop of this.props.active to determine what classes should be applied, etc.
Given that one element is not the parent of another element, you will have to pull up the variable keeping the toggle information up the chain of elements until it resides in one common place.
That is, keep the "active" state variable in an ancestor of the two elements and provide to the Hamburger a callback in the props that, when called, modifies the state of that ancestor component. At the same time, also pass the active state variable down to the MenuOverlay as a prop, and everything should work together.
See here for more information:
https://facebook.github.io/react/tutorial/tutorial.html#lifting-state-up
Specifically,
When you want to aggregate data from multiple children or to have two child components communicate with each other, move the state upwards so that it lives in the parent component. The parent can then pass the state back down to the children via props, so that the child components are always in sync with each other and with the parent.