const Section = ({ text, changeFn }) => {
return(
<>
<Content text={text} changeFn={changeFn}/>
<Content text={text} changeFn={changeFn}/>
<Content text={text} changeFn={changeFn}/>
</>
)
}
const Content = ({ changeFn, text }) => {
const [ textCont, setText ] =useState('text')
return(
<>
<div>{textCont}</div>
<button onClick={changeFn}>Edit</button>
</>
)
}
const Form = ({ changeFn, setContent }) => {
let inputRef = useRef()
const applyChangefn = () => {
setContent(inputRef.current.value)
changeFn()
}
return(
<>
<input ref={inputRef}/>
<button onClick={applyChangefn}>Save</button>
</>
)
}
I want to make a component which exists as example with its own usestate in Content component.
Then i want to send it multiplied by 3 times into Section component.
First question is how to send props between sibling components
Second question is how to make 3 same components which have its own useState state. I mean 3 same components with independent usestate.
I have a component where I need to pass a HTML element as a prop to another element
const MyText = () => {
return (
<>
<h1>Sample heading</h1>
</>
)
}
return (
<div>
<MyComponent Text={MyText} onClose={() => setShow(false)} show={show} />
</div>
);
MyComponent.js
export default function MyComponent(props) {
return (
<>
{props.Text}
</>
);
}
Issue: I'm not getting anything rendered on the screen. Am I missing something here?
There are two ways.
Option 1: Passing a component type (or class if you are coming from OOP background)
const MyText = () => {
return (
<>
<h1>Sample heading</h1>
</>
)
}
return (
<div>
<MyComponent Text={MyText} onClose={() => setShow(false)} show={show} />
</div>
);
const MyComponent = ({ Text }) => {
return (
<>
<Text />
</>
);
}
Option 2: Passing a component (or instance if you are coming from OOP background)
const MyText = () => {
return (
<>
<h1>Sample heading</h1>
</>
)
}
return (
<div>
<MyComponent text={<MyText />} onClose={() => setShow(false)} show={show} />
</div>
);
const MyComponent = ({ text }) => {
return (
<>
{text}
</>
);
}
I'm really new to JS and React. I get this error:
Invalid Hook Call
when I try to make a component appear and disappear when another component is clicked. This is my code:
const RenderList = ({data}) => {
return data.map((option, index) => {
return <Item title={option}/>
});
};
const Header = ({ title, style, press }) => (
<TouchableHighlight onPress={press}>
<Text style={style} >{title}</Text>
</TouchableHighlight>
)
const RenderItem = ( {item} ) => {
console.log(styles)
let dataToShow;
const [listState, setListState] = useState(true);
if (listState){
dataToShow = <RenderList data={item.data}/>
} else {
dataToShow = <Text/>
}
return (
<View style={styles.section}>
<Header title={item.title} style={styles.header} press={setListState(!listState)}/>
{dataToShow}
</View>
)}
EDIT
RenderItem is used in a flat list element as a function. (From what I understand)
const SettingsSection = (props) => {
const db = props.data;
return(
<View>
<FlatList
style={styles.sectionList}
data={db}
renderItem={RenderItem}
keyExtractor={item=>item.title}
ItemSeparatorComponent={FlatListItemSeparator}
/>
</View>
);
}
renderItem, as the name suggests, is a render prop, and as such is called directly (like so: renderItem({item})), not instantiated as a component (like so: <RenderItem item={item}/>).
This translates to React not creating the appropriate rendering "context" for hooks to work. You can make sure your RenderItem function is instantiated as a component by using it like this on the render prop:
<FlatList
style={styles.sectionList}
data={db}
renderItem={item => <RenderItem {...item}/>} // see here!
keyExtractor={item=>item.title}
ItemSeparatorComponent={FlatListItemSeparator}
/>
That way, RenderItem is treated as a component and thus can use hooks.
I think problem is occurring due to setListState(!listState) with press. I suggest you to wrap your state changing method into a function. Because onPress accepts only function type but you are giving it a return statement from hooks.
const RenderList = ({data}) => {
return data.map((option, index) => {
return <Item title={option}/>
});
};
const Header = ({ title, style, press }) => (
<TouchableHighlight onPress={press}>
<Text style={style} >{title}</Text>
</TouchableHighlight>
)
const RenderItem = ( {item} ) => {
console.log(styles)
let dataToShow;
const [listState, setListState] = useState(true);
if (listState){
dataToShow = <RenderList data={item.data}/>
} else {
dataToShow = <Text/>
}
return (
<View style={styles.section}>
<Header
title={item.title}
style={styles.header}
press={()=>{
setListState(!listState)
}}
/>
{dataToShow}
</View>
)}
This is react-window plugin: https://github.com/bvaughn/react-window
I am using this to render simple list of "Rows".
This is Row comp in which I am try to pass function and const idTestProps=''
class Row extends PureComponent {
render() {
const { index, style } = this.props;
let label;
if (itemStatusMap[index] === LOADED) {
label = `Row ${index}`;
} else {
label = "Loading...";
}
return (
<div className="ListItem" style={style}>
{label}
</div>
);
}
}
This is the Container comp which should pass function and one props to the Row comp:
const outerElementType = forwardRef((props, ref) => (
<div ref={ref} onClick={handleClick} {...props} />
));
export default function App() {
return (
<Fragment>
<InfiniteLoader
isItemLoaded={isItemLoaded}
itemCount={1000}
loadMoreItems={loadMoreItems}
>
{({ onItemsRendered, ref }) => (
<List
className="List"
height={150}
itemCount={1000}
itemSize={35}
// This is outerElementType is way to pass some function down to Row
outerElementType={outerElementType}
width={300}
>
{Row}
</List>
)}
</Fragment>
);
I successfully pass 'function' and works but property not.
How to pass props down in same time with function?
This is codesandbox example:
https://codesandbox.io/s/4zqx79nww0
I have never used react-window but maybe you can do something like this:
import React, { forwardRef } from "react";
import ReactDOM from "react-dom";
import { FixedSizeList as List } from "react-window";
import "./styles.css";
const Row = props => ({ index, style }) => (
<div className={index % 2 ? "ListItemOdd" : "ListItemEven"} style={style}>
Row {index} {props.test}
</div>
);
function handleOnWheel({ deltaY }) {
// Your handler goes here ...
console.log("handleOnWheel()", deltaY);
}
const outerElementType = forwardRef((props, ref) => (
<div ref={ref} onWheel={handleOnWheel} {...props} />
));
const Example = () => (
<List
className="List"
height={150}
itemCount={1000}
itemSize={35}
outerElementType={outerElementType}
width={300}
>
{Row({ test: "test" })}
</List>
);
ReactDOM.render(<Example />, document.getElementById("root"));
Can you help me with React.js history.push function?
I have a icon which can be pressed. The onClick calls handleRemoveFavourite function which filters out the current item from localStrorage and sets the updated string to storage. This works fine.
After the storage update is done the program should reroute the user to the root page /favourites. The reroute works well in the bottom example. But how to do this in the handleRemoveFavourites function?
This is the code I would like to have
handleRemoveFavourite = () => {
const { name } = this.props.workout;
let savedStorage = localStorage.saved.split(",");
let cleanedStorage = savedStorage.filter(function(e) {
return e !== name;
});
localStorage.setItem("saved", cleanedStorage.toString());
history.push("/favourites")
};
renderHeartIcon = () => {
return (
<Route
render={({ history }) => (
<Rating
icon="heart"
defaultRating={1}
maxRating={1}
onClick={this.handleRemoveFavourite}
/>
)}
/>
);
};
The rerouting works fine with just this:
renderHeartIcon = () => {
return (
<Route
render={({ history }) => (
<Rating
key={1}
icon="heart"
defaultRating={1}
maxRating={1}
size="large"
onClick={() => history.push("/favourites")}
/>
)}
/>
);
};
The whole component looks like this:
import React from "react";
import {
Container,
Grid,
Icon,
Label,
Table,
Header,
Rating,
Segment
} from "semantic-ui-react";
import { Link, Route } from "react-router-dom";
export default class WorkoutComponent extends React.PureComponent {
renderChallengeRow = workouts => {
let { reps } = this.props.workout;
reps = reps.split(",");
return workouts.map((item, i) => {
return (
<Table.Row key={item.id}>
<Table.Cell width={9}>{item.name}</Table.Cell>
<Table.Cell width={7}>{reps[i]}</Table.Cell>
</Table.Row>
);
});
};
handleRemoveFavourite = () => {
const { name } = this.props.workout;
let savedStorage = localStorage.saved.split(",");
let cleanedStorage = savedStorage.filter(function(e) {
return e !== name;
});
localStorage.setItem("saved", cleanedStorage.toString());
// history.push("/favourites");
};
renderHeartIcon = () => {
return (
<Route
render={({ history }) => (
<Rating
key={1}
icon="heart"
defaultRating={1}
maxRating={1}
size="large"
onClick={this.handleRemoveFavourite}
/>
)}
/>
);
};
render() {
const { name, workouts } = this.props.workout;
const url = `/savedworkout/${name}`;
return (
<Grid.Column>
<Segment color="teal">
<Link to={url}>
<Header as="h2" to={url} content="The workout" textAlign="center" />
</Link>
<Table color="teal" inverted unstackable compact columns={2}>
<Table.Body>{this.renderChallengeRow(workouts)}</Table.Body>
</Table>
<br />
<Container textAlign="center">
<Label attached="bottom">{this.renderHeartIcon()}</Label>
</Container>
</Segment>
<Link to="/generate">
<Icon name="angle double left" circular inverted size="large" />
</Link>
</Grid.Column>
);
}
}
Since you are using react-router you can use withRouter to achive this.
import { withRouter } from 'react-router-dom'
Wrap the with class name with withRouter.
Like this:
instead of doing like this:
export default class App .....
Separate this like:
class App ...
At the end of the line:
export default withRouter(App)
Now you can use like this:
handleRemoveFavourite = () => {
const { name } = this.props.workout;
let savedStorage = localStorage.saved.split(",");
let cleanedStorage = savedStorage.filter(function(e) {
return e !== name;
});
localStorage.setItem("saved", cleanedStorage.toString());
this.props.history.push("/favourites");
};
You can remove Route from renderHearIcon():
renderHeartIcon = () => {
return (
<Rating
key={1}
icon="heart"
defaultRating={1}
maxRating={1}
size="large"
onClick={this.handleRemoveFavourite}
/>
);
};
onClick={this.handleRemoveFavourite}
=>
onClick={()=>this.handleRemoveFavourite(history)}
handleRemoveFavourite = () => {
=>
handleRemoveFavourite = (history) => {
The problem you are facing is, you are providing <Route> with the history prop, so that it is propagated on the component function call. But you are not propagating it to the handleRemoveFavourite function.
You'll need to wrap this. handleRemoveFavourite in an anonymous function call. Like
onClick={() => this.handleRemoveFavourite(history)}
and then accept it as a valid argument in your function
handleRemoveFavourite = (history) => {...}
that should solve it
Changing to this.props.history.push("/favourites"); should works.
EDITED
Is your component inside a Route? If so, this.props.history will works. I tested changing your class to run on my project.
import React from 'react';
import ReactDOM from 'react-dom';
import {
Container,
Grid,
Icon,
Label,
Table,
Header,
Rating,
Segment
} from "semantic-ui-react";
import { Link, Route, BrowserRouter as Router } from "react-router-dom";
export default class WorkoutComponent extends React.Component {
static defaultProps = {
workout: {
reps: "1,2,3,4",
workouts: []
},
}
renderChallengeRow = workouts => {
let { reps } = this.props.workout;
reps = reps.split(",");
return workouts.map((item, i) => {
return (
<Table.Row key={item.id}>
<Table.Cell width={9}>{item.name}</Table.Cell>
<Table.Cell width={7}>{reps[i]}</Table.Cell>
</Table.Row>
);
});
};
handleRemoveFavourite = () => {
const { name } = this.props.workout;
//let savedStorage = localStorage.saved.split(",");
// let cleanedStorage = savedStorage.filter(function(e) {
// return e !== name;
// });
// localStorage.setItem("saved", cleanedStorage.toString());
this.props.history.push("/favourites");
};
renderHeartIcon = () => {
return (
<Route
render={({ history }) => (
<Rating
key={1}
icon="heart"
defaultRating={1}
maxRating={1}
size="large"
onClick={this.handleRemoveFavourite}
/>
)}
/>
);
};
render() {
console.log(this.props)
const { name, workouts } = this.props.workout;
const url = `/savedworkout/${name}`;
return (
<Grid.Column>
<Segment color="teal">
<Link to={url}>
<Header as="h2" to={url} content="The workout" textAlign="center" />
</Link>
<Table color="teal" inverted unstackable compact columns={2}>
<Table.Body>{this.renderChallengeRow(workouts)}</Table.Body>
</Table>
<br />
<Container textAlign="center">
<Label attached="bottom">{this.renderHeartIcon()}</Label>
</Container>
</Segment>
<Link to="/generate">
<Icon name="angle double left" circular inverted size="large" />
</Link>
</Grid.Column>
);
}
}
ReactDOM.render(
<Router>
<Route path="/" component={ WorkoutComponent }/>
</Router>, document.getElementById('root'));