Hello I have a React app that has a menu.
The state has 2 arrays one with the names of the menu items as string which i managed to render with the map function and I would like to enumerate for each menu item on the onClick attribute a function from the array of functions that I have in my state.
This is the code:
class MeniuPF extends Component {
constructor(props) {
super(props);
this.state = {
collapseID: '',
lista:[
'Adeverinta venit','Bilant anual','Bilant semestrial',
],
listafunctii:[
this.toggleAdeverintaVenit,
this.toggleBilantAnual,
this.toggleBilantSemestrial,
],
listaMesaje:false,
bilantAnual:false,
bilantSemestrial:false,
};
this.toggleListaMesaje = this.toggleListaMesaje.bind(this);
this.toggleBilantAnual = this.toggleBilantAnual.bind(this);
this.toggleBilantSemestrial = this.toggleBilantSemestrial.bind(this);
}
toggleCollapse = collapseID => () => {
this.setState(prevState => ({
collapseID: prevState.collapseID !== collapseID ? collapseID : ''
}));
};
toggleBilantAnual() {
this.setState({bilantAnual:!this.state.bilantAnual})
this.setState({bilantSemestrial:false})
this.setState({adeverintaVenit:false})
this.setState(prevState => ({
collapseID: prevState.collapseID !== this.state.collapseID ? this.state.collapseID : ''
}));
}
toggleBilantSemestrial() {
this.setState({bilantAnual:false})
this.setState({bilantSemestrial:!this.state.bilantSemestrial})
this.setState({adeverintaVenit:false})
this.setState(prevState => ({
collapseID: prevState.collapseID !== this.state.collapseID ? this.state.collapseID : ''
}));
}
toggleAdeverintaVenit() {
this.setState({bilantAnual:false})
this.setState({bilantSemestrial:false})
this.setState({adeverintaVenit:!this.state.adeverintaVenit})
this.setState(prevState => ({
collapseID: prevState.collapseID !== this.state.collapseID ? this.state.collapseID : ''
}));
}
check() {
this.state.listafunctii.map((list) =>
console.log(this.state.listafunctii[0])
)
};
render() {
return (
<div>
<Router>
<MDBContainer>
<MDBNavbar
color='light-blue lighten-4'
style={{ marginTop: '20px' }}
light
>
<MDBContainer>
<MDBNavbarBrand>Declaratii persoane juridice</MDBNavbarBrand>
<MDBNavbarToggler
onClick={this.toggleCollapse('navbarCollapse1')}
/>
<MDBCollapse
id='navbarCollapse1'
isOpen={this.state.collapseID}
navbar
>
<MDBNavbarNav left>
<MDBNavItem >
{this.state.lista.map((list,i) =>
<MDBNavLink to='#!' onClick={this.state.listafunctii[i}>{list}</MDBNavLink>
)}
</MDBNavItem>
</MDBNavbarNav>
</MDBCollapse>
</MDBContainer>
</MDBNavbar>
</MDBContainer>
</Router>
<br></br>
{this.state.listaMesaje ? <ListaMesaje/>:null}
{this.state.bilantAnual ? <BilantAnual/>:null}
{this.state.bilantSemestrial ? <BilantSemestrial/>:null}
</div>
);
}
}
export default MeniuPF;
I have at onClick attribute this.state.listafunctii[0] because I thought it would enumerate the functions that I have written in the state array but I have checked what is returning with the check method and in the console it shows me the whole function. When I click a menu item it gives me this error:
TypeError: Cannot read property 'setState' of undefined
So it calls the function but it gives me this error...and when I console.log the map of the array it return me what is contained in the function. I would like to enumerate only the function call eg: this.toggleAdeverintaVenit only this to show in the onClick attribute.
Thanks in advance
I found the answer...I moved the binding of the methods before the state and it works...and adding in the onClick attribute
onClick={this.state.listafunctii[i]()}
Related
I have a page that renders questions that have been posted. I want to create a button that displays only answered questions based on the state = {isAnswered: true}.
Is the state isAnswered is true then onClick will display answered questions only where isAnswered is set to true in the object.
How can I used this Filter button to conditionally render these based on their state.
Should the function be stored as constant called in the render function or before this?
this.state.posts is an array of these objects on the back end:
Here is what I have attempted.
class Posts extends Component {
state = {
posts: []
}
render () {
let posts = <p style={{ textAlign: 'center' }}>Something went wrong!</p>;
let {isAnswered} = this.state;
const renderAuthButton = () => {
if (isAnswered === true) {
if ( !this.state.error ) {
posts = this.state.posts.map( (post) => {
return (
<Post
key={post.key}
id={post.key}
title={post.title}
type={post.type}
body={post.body}
answer={post.answer}
onChange={(value, id) => this.postAnswerHandler(value,id)}
clicked={(body) => this.displayAnswerHandler(body)}
/>
);
} );
}
}
}
}
return (
<button onClick={renderAuthButton()}>Filter</button>
{posts}
)
You are misinterpreting your data structure. this.state has a property this.state.posts which is an array. Each element in the array is an object with multiple properties including isAnswered.
When you do this:
let {isAnswered} = this.state;
You are looking for a property this.state.isAnswered which does not exist. There is no top-level isAnswered property. It is something that exists within each post object and is different for every post. So you need to be looking at isAnswered inside of your loop.
There's honestly a lot that's weird and backwards here. Don't create a callback inside of render()! Don't return JSX from a callback!
Here's my attempt to clean it up. I am adding a property to this.state which tells us whether or not to filter the posts. Clicking the button changes this.state.isFiltered. The render function renders appropriately based on the current state.
class Posts extends Component {
state = {
posts: [],
isFiltered: false,
isError: false
};
async componentDidMount() {
// do your API fetch and set the state for `posts` and `isError`
try {
const fetchedPosts = someApiFunction();
this.setState({
posts: fetchedPosts
});
} catch (error) {
this.setState({
isError: true
});
}
}
onClickFilter = () => {
// toggles filter on and off
this.setState((prevState) => ({
isFiltered: !prevState.isFiltered
}));
};
render() {
if (this.state.isError) {
return <p style={{ textAlign: "center" }}>Something went wrong!</p>;
}
// show only answered posts if isFiltered is true, or all posts if false
const visiblePosts = this.state.isFiltered
? this.state.posts.filter((post) => post.isAnswered)
: this.state.posts;
return (
<>
<button onClick={this.onClickFilter}>Filter</button>
{visiblePosts.map((post) => {
return (
<Post
key={post.key}
id={post.key}
title={post.title}
type={post.type}
body={post.body}
answer={post.answer}
onChange={(value, id) => this.postAnswerHandler(value, id)}
clicked={(body) => this.displayAnswerHandler(body)}
/>
);
})}
</>
);
}
}
I am trying to create a component where I have a bunch of boxes from an array, that can be turned 'on' and 'off' when each one is individually clicked.
Currently, only a single item from the array can be switched 'on' (shown by the item turning green), however, I would like to be able to turn each item on/off individually.
Interacting with one element should not affect any of the others.
How do I achieve this?
My click event:
handleOnClick = (val, i) => {
this.setState({active: i}, () => console.log(this.state.active, 'active'))
}
Rendering the boxes:
renderBoxes = () => {
const options = this.state.needsOptions.map((val, i) => {
return (
<button
key={i}
style={{...style.box, background: i === this.state.active ? 'green' : ''}}
onClick={() => this.handleOnClick(val, i)}
>
{val}
</button>
)
})
return options
}
Here's a Codepen
What I would do is to create a Box component with its own active state, and pass this to the map in renderBoxes. The benefit of doing it this way is that each Box component will have its own state independent of the parent. That way you can have more than one component as active.
so...
class Box extends React.Component {
constructor(props){
super(props)
this.state={
active: false
}
}
clickHandler = () => {
this.setState({active: !this.state.active})
}
render(){
const { key, children }= this.props
return (
<button
key={key}
style={{...style.box, background: this.state.active ? 'green' : ''}}
onClick={() => this.clickHandler()}
>
{children}
</button>
)
}
}
then have renderBoxes be...
renderBoxes = () => {
const options = this.state.needsOptions.map((val, i) => {
return (
<Box
key={i}
>
{val}
</Box>
)
})
return options
}
here is the codepen I forked off yours.
I want to set the active class dynamically in react.js but it's not working!
I'm using the setState() method to change the selected item.
this line of code not work .
className={selectedCategoryId === item.id ? 'active' : ''}
I think the setState() function does not work correctly...
const {open, selectedProduct, productCategory, filteredProducts, selectedCategoryId} = this.state;
const categoryItems = productCategory.map((item) =>
<a key={item.id}
onClick={() => this.handleFilter(item.id)}
className={selectedCategoryId === item.id ? 'active' : ''}
// className={()=>this.isActive(item.id)}
className="pointer"
>{item.value}</a>
);
this does not change the class:
handleFilter = (id) => {
const filteredItem = this.state.productList.filter(x => x.categoryId == id);
this.setState({filteredProducts: filteredItem, selectedCategoryId: id});
}
but this change the className correctly when select I all tags:
handleRemoveFilter = () => {
this.setState({filteredProducts: this.state.productList, selectedCategoryId: 0});
}
//-------------------------------
<div className="tag-list">
<a onClick={this.handleRemoveFilter}
className="pointer"
className={ this.state.selectedCategoryId === 0 ? 'active' : ''}
>All tags</a>
{categoryItems}
</div>
If setState() works well, try this :
<a onClick={this.handleRemoveFilter}
className={ this.state.selectedCategoryId === 0 ? 'pointer active' : 'pointer'}
>All tags</a>
One of the most common ways is to use classnames which you can conditionally joining classNames together
var classNames = require('classnames');
class Button extends React.Component {
// ...
render () {
var btnClass = classNames({
btn: true,
'btn-pressed': this.state.isPressed,
'btn-over': !this.state.isPressed && this.state.isHovered
});
return <button className={btnClass}>{this.props.label}</button>;
}
}
store classname in state along with selected item. You can just update the classname in state whenever required.
for eg,
<a key={item.id}
onClick={() => this.handleFilter(item.id)}
className={this.state.activeClassName}
where active classname can be updated inside handlefilter
We can toggle class name dynamically like below,
const [islight, setIslight] = useState(false)
const toggle = () => {
setIslight(!islight)
}
return (
<div className={`block ${islight ? "blocklight" : "blockdark"}`}>
<h2>Hello World</h2>
</div>
)
I have a <Select> component from react-select renders a couple options to a dropdown, these options are fetched from an api call, mapped over, and the names are displayed. When I select an option from the dropdown the selected name does not appear in the box. It seems that my handleChange method is not firing and this is where I update the value of the schema name:
handleChange = value => {
// this is going to call setFieldValue and manually update values.dataSchemas
this.props.onChange("schemas", value);
This is not updating the value seen in the dropdown after something is selected.
I'm not sure if I'm passing the right thing to the value prop inside the component itself
class MySelect extends React.Component {
constructor(props) {
super(props);
this.state = {
schemas: [],
fields: [],
selectorField: ""
};
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
axios.get("/dataschemas").then(response => {
this.setState({
schemas: response.data.data
});
console.log(this.state.schemas);
});
}
handleChange = value => {
// this is going to call setFieldValue and manually update values.dataSchemas
this.props.onChange("schemas", value);
const schema = this.state.schemas.find(
schema => schema.name === value.target.value
);
if (schema) {
axios.get("/dataschemas/2147483602").then(response => {
this.setState({
fields: response.data.fields
});
console.log(this.state.fields);
});
}
};
updateSelectorField = e => {
this.setState({ selectorField: e.target.value });
};
handleBlur = () => {
// this is going to call setFieldTouched and manually update touched.dataSchemas
this.props.onBlur("schemas", true);
};
render() {
return (
<div style={{ margin: "1rem 0" }}>
<label htmlFor="color">
DataSchemas -- triggers the handle change api call - (select 1){" "}
</label>
<Select
id="color"
options={this.state.schemas}
isMulti={false}
value={this.state.schemas.find(
({ name }) => name === this.state.name
)}
getOptionLabel={({ name }) => name}
onChange={this.handleChange}
onBlur={this.handleBlur}
/>
{!!this.props.error && this.props.touched && (
<div style={{ color: "red", marginTop: ".5rem" }}>
{this.props.error}
</div>
)}
</div>
);
}
}
I have linked an example showing this issue.
In your handleChange function you are trying to access value.target.value. If you console.log(value) at the top of the function, you will get:
{
id: "2147483603"
selfUri: "/dataschemas/2147483603"
name: "Book Data"
}
This is the value that handChange is invoked with. Use value.name instead of value.target.value.
As part of a technical test, I've been asked to write an autocomplete input in React. I've done this but I'd now like to add the functionality of navigating up and down the rendered list with the arrow keys. I've done some extensive Googling and found nothing React specific apart from npm packages.
To be clear, I'm looking for something like this but for React: https://www.w3schools.com/howto/howto_js_autocomplete.asp
All I basically need is the arrow button functionality, I've got everything else working fine.
Cheers
Here's an example that I tried but couldn't get working.
export default class Example extends Component {
constructor(props) {
super(props)
this.handleKeyDown = this.handleKeyDown.bind(this)
this.state = {
cursor: 0,
result: []
}
}
handleKeyDown(e) {
const { cursor, result } = this.state
// arrow up/down button should select next/previous list element
if (e.keyCode === 38 && cursor > 0) {
this.setState( prevState => ({
cursor: prevState.cursor - 1
}))
} else if (e.keyCode === 40 && cursor < result.length - 1) {
this.setState( prevState => ({
cursor: prevState.cursor + 1
}))
}
}
render() {
const { cursor } = this.state
return (
<Container>
<Input onKeyDown={ this.handleKeyDown }/>
<List>
{
result.map((item, i) => (
<List.Item
key={ item._id }
className={cursor === i ? 'active' : null}
>
<span>{ item.title }</span>
</List.Item>
))
}
</List>
</Container>
)
}
}
And here is my code:
class Search extends Component {
constructor(props) {
super(props);
this.state = {
location: '',
searchName: '',
showSearch: false,
cursor: 0
};
}
handleKeyPress = e => {
const { cursor, searchName } = this.state;
// arrow up/down button should select next/previous list element
if (e.keyCode === 38 && cursor > 0) {
this.setState(prevState => ({
cursor: prevState.cursor - 1
}));
} else if (e.keyCode === 40 && cursor < searchName.length - 1) {
this.setState(prevState => ({
cursor: prevState.cursor + 1
}));
}
};
render() {
const { searchName, location } = this.state;
return (
<div className="Search">
<h1>Where are you going?</h1>
<form id="search-form" onSubmit={this.handleSubmit}>
<label htmlFor="location">Pick-up Location</label>
<input
type="text"
id="location"
value={location}
placeholder="city, airport, station, region, district..."
onChange={this.handleChange}
onKeyUp={this.handleKeyUp}
onKeyDown={this.handleKeyPress}
/>
{this.state.showSearch ? (
<Suggestions searchName={searchName} />
) : null}
<button value="submit" type="submit" id="search-button">
Search
</button>
</form>
</div>
);
}
Code that renders the list from the restful API:
.then(res =>
this.setState({
searchName: res.data.results.docs.map(array => (
<a href="#">
<div
key={array.ufi}
className="locations"
>
{array.name}
</div>
</a>
))
})
);
Since you are defining a function as handleKeyDown(e) {...} the context this is not pointing to the context of class instance, the context will be supplied by onKeyDown (and I suppose it's window as this)
So, you have 2 ways to go:
declare your function as handleKeyDown = (e) => {...}
bind handleKeyDown context to component instance like onKeyDown={this.handleKeyDown.bind(this)}
Also, don't forget that you may want a mod items.length counter, meaning when your press down and the last item is already selected, it would go to the first item.
Your api usage with storing markdown into the state is just a terrible thing to do. You don't have access to these strings anymore, instead save it as a plain array. Pass it where you need it, and use it to create jsx there.
Also, you don't use cursor from your state at all.