How Do I Dynamically Set State Of Variable For Render Function - javascript

For My Class We Are Making A Website With React And Neither Me Or my Group Can Figure Out How To Just Render A Function In A Variable State And Make It Dynamic
My Code Is As Follows:
class App extends React.Component {
constructor(props)
{
super(props)
this.state = {
screen: this.home(),
movies: []
}
}
home = () =>{
this.state.movies.map((movie)=>{
return(
<div>
<Popular
title={movie.title}
rating={movie.vote_average}
poster={movie.poster_path}
desc={movie.overview}
/>
</div>
)
})
}
render(){
return(
<div>{this.state.screen}</div>
)
}
}
When I Run This The Error Reads
TypeError: Cannot read property 'movies' of undefined
You Can Assume That The Variable in State Movies Is Filled With An Array Of Movies Set By An API
Edit: The End Result I'm Attempting To Achieve Is To Return A Variable Or State Which Can Hold A Function Which Would Be The Different Screens/Pages To Be Rendered

If your movies array filled with data from any API call, then you can directly use that array to render the data,
class App extends React.Component {
constructor(props)
{
super(props)
this.state = {
movies: []
}
}
render(){
return(
<div>
{
this.state.movies.map((movie)=>{
return(
<div>
<Popular
title={movie.title}
rating={movie.vote_average}
poster={movie.poster_path}
desc={movie.overview}
/>
</div>
)
})
}
</div>
)
}
}

The root cause here is that this.state is not initialized when you're using it the home() invocation in the constructor.
Either way, you're not supposed to store rendered content within state.
Based on the comment, here's a refactoring, but I would recommend looking into an actual router like react-router instead.
const HomeView = ({ movies }) =>
movies.map(movie => (
<div>
<Popular
title={movie.title}
rating={movie.vote_average}
poster={movie.poster_path}
desc={movie.overview}
/>
</div>
));
const FavoritesView = ({ movies }) => <>something else...</>;
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
movies: [],
view: "home",
};
}
render() {
let view = null;
switch (this.state.view) {
case "home":
view = <HomeView movies={this.state.movies} />;
break;
case "favorites":
view = <FavoritesView movies={this.state.movies} />;
break;
}
return (
<div>
<a href="#" onClick={() => this.setState({ view: "home" })}>
Home
</a>
<a href="#" onClick={() => this.setState({ view: "favorites" })}>
Favorites
</a>
{view}
</div>
);
}
}

Related

Passing Function parameter to another component in react

I a learning react and stuck at this place. I am creating an app In which user will see a list of product with different id and name. I have created another component in which the detail of the product will open . I am collection the id and value of the particular product in my addList component by onClick function. And now i want to send those value in DetailList component so that i can show the detail of that particular product.
A roadmap like
Add list -> (user click on a product) -> id and name of the product passes to the DetailList component -> Detail list component open by fetching the product detail.
Here is my code of Add list component
export default class Addlist extends Component {
constructor(props) {
super(props)
this.state = {
posts : []
}
}
passToDetailList(id) {
console.log( id)
}
async componentDidMount() {
axios.get('http://localhost:80/get_add_list.php')
.then(response => {
console.log(response);
this.setState({posts: response.data})
})
.catch(error => {
console.log(error);
})
}
render() {
const { posts } = this.state;
// JSON.parse(posts)
return (
<Fragment>
<div className="container" id="listOfAdd">
<ul className="addUl">
{
posts.map(post => {
return (
<li key={post.id}>
<div className="row">
<div className="col-md-4">
<img src={trialImage} alt=""></img>
</div> {/* min col end */}
<div className="col-md-8">
<b><h2>{post.add_title}</h2></b>
{/* This button is clicked by user to view detail of that particular product */}
<button onClick={() => this.passToDetailList(post.id)}>VIEW</button>
</div> {/* min col end */}
</div> {/* row end */}
</li>
);
})}
</ul>
</div>{/* container end */}
</Fragment>
)
}
}
You should pass the data through the routes -
<Route path="/details/:id" component={DetailList} /> // router config
passToDetailList(id) {
this.props.history.push('/details/'+id)
}
and then in the DetailList Component, you can access the value through -
console.log(this.props.match.params.id) - //here is the passed product Id
You need to elevate the state for id to a common parent between AddList and DetailList and then create a function in parent component to set the id and pass the id and setId function to your AddList Component through props , then just use setId function to set the id state in passToDetailList function.
finally you can use the id in your DetailList Component to fetch its details
so Here is how your AddList Component would look like:
export default class Addlist extends Component {
constructor(props) {
super(props);
this.state = {
posts: []
};
}
passToDetailList(id) {
this.props.setId(id);
}
// The rest of your code
}
and here is how your DetailList Component will look like:
export default class DetailList extends Component {
componentDidMount(){
// Use the id to fetch its details
console.log(this.props.id)
}
}
and finally here is your CommonParent Component:
export default class CommonParent extends Component {
constructor(props) {
super(props);
this.state = {
id: ''
};
this.setId = this.setId.bind(this);
}
setId(id){
this.setState({
id
})
}
render(){
return(
<>
<AddList setId={this.setId} />
<DetailList id={this.state.id} />
</>
)
}
}
if your Components are very far from each other in component tree you can use react context or redux for handling id state

How to create an object from another class, and push it into the state array?

I'm currently working on a React application, where I two classes - let's call them class App and class Container. Basically, class App has a state array, and I want to have many Container objects in this array.
class Container extends React.Component{
render(){
return(
<img src= {this.props.url} />
);
}
}
class App extends React.Component{
constructor(props){
super(props);
this.state = {
url: ""
data: []
}
}
handleSubmit(event){
event.preventDefault();
//I WANT TO BE ABLE TO MAKE A NEW CONTAINER, AND PASS THE URL AS PROPS.
// THEN, I WANT TO ADD THAT CONTAINER TO THE ARRAY.
this.setState({
data: url = this.state.url, id = 'a'
});
}
render(){
return (
<form onSubmit={this.handleSubmit}>
<label htmlFor="url">url:</label>
<input
type = "text"
name = "url"
value = {this.state.url}
onChange = {this.handleChange}
/>
</form>
)
}
}
In the function handleSubmit() above, I want to add a new container containing the props URL to the array. How would I do this?
don't mutate the state
you just need url in the state, not the whole container
use setState to modify the state
consider using spread operator (...) for concatenation
I don't see handleChange in your code
class Container extends React.Component {
render() {
return <img src={this.props.url} />;
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
url: "",
containers: []
};
}
handleChange = e => {
this.setState({
url: e.target.value
});
};
handleSubmit = event => {
event.preventDefault();
if (this.state.url) {
this.setState({
containers: [...this.state.containers, this.state.url],
url: ""
});
}
};
render() {
const { url, containers } = this.state;
return (
<div>
<form onSubmit={this.handleSubmit}>
<label htmlFor="url">url:</label>
<input
type="text"
name="url"
value={url}
onChange={this.handleChange}
/>
<button>submit</button>
</form>
<h2>Containers:</h2>
<div>
{!containers.length && <i>no urls added</i>}
{containers.map((_url, i) => (
<Container key={i} url={_url} />
))}
</div>
</div>
);
}
}
render(<App />, document.getElementById("root"));
Working Example:
https://stackblitz.com/edit/react-g72uej

Call a method pass from parent component to child component in React

I have a parent stateful react component that has a function that will change when an html span is clicked within the child component. I want to pass that method to the child component and call it when the snap is clicked I then want to pass it back up to the parent component and updated the state based on what is passed back up. I am having trouble passing down the method and calling it within the child component...
parent component:
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
dates: [],
workouts: [],
selectedDate: '',
selectedWorkouts: []
}
this.updateDateAndWorkouts = this.updateDateAndWorkouts.bind(this)
axios.defaults.baseURL = "http://localhost:3001"
}
updateDateAndWorkouts = () => {
console.log('clicked')
}
render() {
return (
<div>
<DateBar data={this.state.dates}/>
<ClassList workouts={this.state.selectedWorkouts} updateDate={this.updateDateAndWorkouts}/>
</div>
)
}
This is the child component:
export default function Datebar(props) {
return (
<div>
{props.data.map((day, index) => {
return (
<div key={index}>
<span onClick={props.updateDate}>
{day}
</span>
</div>
)
})}
</div>
)
}
What I want to happen is when the method is called in thechild component, it calls the method that was passed and pass the text within the span div...
You have to actually pass function to child component
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
dates: [],
workouts: [],
selectedDate: '',
selectedWorkouts: []
}
this.updateDateAndWorkouts = this.updateDateAndWorkouts.bind(this)
axios.defaults.baseURL = "http://localhost:3001"
}
updateDateAndWorkouts = () => {
console.log('clicked')
}
render() {
return (
<div>
<DateBar data={this.state.dates} updateDate={this.updateDateAndWorkouts} />
<ClassList workouts={this.state.selectedWorkouts} updateDate={this.updateDateAndWorkouts}/>
</div>
)
}
You have to call that method in child component
props.updateDate()
export default function Datebar(props) {
return (
<div>
{props.data.map((day, index) => {
return (
<div key={index}>
<span onClick={props.updateDate()}>
{day}
</span>
</div>
)
})}
</div>
)
}

Loop over all instances of component, log each state

I'm building out a simple drum machine application using ReactJS and could use some help understanding how to loop through all instances of a component while outputting each instance's state.
The application UI shows 16 columns of buttons, each containing 4 unique drum rows. There is a "SixteenthNote.js" component which is essentially on column containing each "Drum.js" instance. In the "DrumMachine.js" module, I am outputting "SixteenthNote.js" 16 times to display one full measure of music. When you click on a drum button, that drum's value is pushed into the SixteenthNote' state array. This is all working as intended.
The last part of this is to create a "Play.js" component which, when clicked, will loop through all of the SixteenthNote instances and output each instance's state.
Here is the "DrumMachine.js" module
class DrumMachine extends Component {
constructor(props) {
super(props);
this.buildKit = this.buildColumns.bind(this);
this.buildLabels = this.buildLabels.bind(this);
this.buildAudio = this.buildAudio.bind(this);
this.state = {
placeArray: Array(16).fill(),
drumOptions: [
{type: 'crash', file: crash, title: 'Crash'},
{type: 'kick', file: kick, title: 'Kick'},
{type: 'snare', file: snare, title: 'Snare'},
{type: 'snare-2', file: snare2, title: 'Snare'}
]
}
}
buildLabels() {
const labelList = this.state.drumOptions.map((sound, index) => {
return <SoundLabel title={sound.title} className="drum__label" key={index} />
})
return labelList;
}
buildColumns() {
const buttonList = this.state.placeArray.map((object, index) => {
return <SixteenthNote columnClassName="drum__column" key={index} drumOptions={this.state.drumOptions}/>
});
return buttonList;
}
buildAudio() {
const audioList = this.state.drumOptions.map((audio, index) => {
return <Audio source={audio.file} drum={audio.type} key={index}/>
})
return audioList;
}
render() {
return (
<div>
<div className={this.props.className}>
<div className="label-wrapper">
{this.buildLabels()}
</div>
<div className="drum-wrapper">
{this.buildColumns()}
</div>
</div>
<div className="audio-wrapper">
{this.buildAudio()}
</div>
</div>
)
}
}
Here is "SixteenthNote.js" module
class SixteenthNote extends Component {
constructor(props) {
super(props);
this.buildColumn= this.buildColumn.bind(this);
this.buildDrumOptions = this.buildDrumOptions.bind(this);
this.updateActiveDrumsArray = this.updateActiveDrumsArray.bind(this);
this.state = {
activeDrums: []
}
}
buildDrumOptions() {
return this.props.drumOptions;
}
updateActiveDrumsArray(type) {
let array = this.state.activeDrums;
array.push(type);
this.setState({activeDrums: array});
}
buildColumn() {
const placeArray = this.buildDrumOptions().map((button, index) => {
return <Drum buttonClassName="drum__button" audioClassName="drum__audio" type={button.type} file={button.file} key={index} onClick={() => this.updateActiveDrumsArray(button.type)}/>
})
return placeArray;
}
render() {
return (
<div className={this.props.columnClassName}>
{this.buildColumn()}
</div>
)
}
}
Here is the "Drum.js" module
class Drum extends Component {
constructor(props) {
super(props);
this.clickFunction = this.clickFunction.bind(this);
this.state = {
clicked: false
}
}
drumHit(e) {
document.querySelector(`.audio[data-drum=${this.props.type}]`).play();
this.setState({clicked:true});
}
clickFunction(e) {
this.state.clicked === false ? this.drumHit(e) : this.setState({clicked:false})
}
render() {
const drumType = this.props.type;
const drumFile = this.props.file;
const buttonClasses = `${this.props.buttonClassName} drum-clicked--${this.state.clicked}`
return (
<div onClick={this.props.onClick}>
<button className={buttonClasses} data-type={drumType} onClick={this.clickFunction}></button>
</div>
)
}
}
You will need to contain the information about the activeDrums in your DrumMachine component.
That means:
In your DrumMachine component you create the state activeDrums like you have in your SixteenthNote.js. You will need to put your updateActiveDrumsArray function to your drumMachine component as well.
Then you pass this function to your SixteenthNote component like:
<SixteenthNote columnClassName="drum__column" key={index} drumOptions={this.state.drumOptions} onDrumsClick={this.updateActiveDrumsArray} />
After doing so, you can access that function via props. So, in your SixteenthNote component it should look like:
<Drum buttonClassName="drum__button" audioClassName="drum__audio" type={button.type} file={button.file} key={index} onClick={() => this.props.onDrumsClick(button.type)}/>
(Don't forget to get rid of the unneccessary code.)
With this, you have your activeDrums state in DrumMachine containing all the active drums. This state you can then send to your play component and do the play action there.

State is always one step behind (setState async problems) - React js

I have three components:
PageBuilder - is basically a form where the user adds a page name and selects some items.
PageList - stores all pages the user has created in state and renders that state as a list
PageUpdater - takes the form info from PageBuilder and adds it to PageList
The problem I'm having is that the state of each component is always one step behind. I realise that this is because setState is asynchronous but I'm not sure what's the best way to get around that. I've read a few possible solutions but I'm not sure how best to implement them in my setup. Can anyone advise?
Here is PageBuilder (I've cut it down for clarity):
constructor(props){
super(props);
this.state = {
pageTitle: '', pageDesc:'', items: [], id:''
};
}
updateTitle = (e) => {
this.setState({pageTitle: e.target.value});
}
updateDesc = (e) => {
this.setState({pageDesc: e.target.value});
}
addNewPage = () => {
let info = {...this.state};
this.props.callBack(info);
}
render() {
return (
<input className="pageTitleField" type="text" placeholder="Page Title"
value={this.state.pageTitle} onChange={this.updateTitle}></input>
<textarea className="pageDescField" placeholder="Page description..."
onChange={this.updateDesc}></textarea>
<button onClick={this.addNewPage}>New Page</button>
)
}
PageUpdater:
export class PageUpdater extends React.Component{
constructor(props){
super(props);
this.state={
data: ''
}
}
updatePageList = (pageAdded) =>{
this.setState({data:pageAdded});
console.log(this.state)
}
render(){
return(
<div>
<PageBuilder callBack={this.updatePageList} />
<PageList addToList={this.state.data} />
</div>
)}}
PageList:
export class PageList extends React.Component{
constructor(props){
super(props);
this.state = {pages:''}
}
componentWillReceiveProps(props) {
this.setState({pages: [...this.state.pages, this.props.addToList]})
}
getPages = () => {
var pages = []
for(var key in this.state.pages){
pages.push(this.state.pages[key].pageTitle)}
return pages // Return an array with the names
}
render(){
return(
<div>
{this.getPages().map((page, index) => <li key={index}>{page}
</li>)}
</div>
)}}
Inside of componentWillReceiveProps this.props refers to the previous version of props. But what you need is to use the latest version of props.
Instead of
componentWillReceiveProps(props) {
this.setState({pages: [...this.state.pages, this.props.addToList]})
}
You should write
componentWillReceiveProps(nextProps) {
this.setState({pages: [...this.state.pages, nextProps.addToList]}) // notice the difference this.props vs nextProps
}

Categories