graveltrunk
graveltrunk

Reputation: 75

Reusing multiple instances of react component with different props

So I have a child component that I want to render multiple instances of in a parent container component. Passing in different props to each so they display differently.

What is happening is that they are both being rendered with the last instance of the props in the script being read into both instances. Thus the both components below end up with placeHolder==='Describe yourself' Is there a work around for this so that they will each be injected with their props in turn exclusively?

           <ButtonMode 
              open={this.state.open}
              handleClose={this.handleClose}
              buttonName='Update'
              modalOpen={this.modalOpen}    
              placeHolder="New picture url"
              change={this.handlePicture}
              label='URL'
            />

           <ButtonMode 
              open={this.state.open}
              handleClose={this.handleClose}
              buttonName='Update'
              modalOpen={this.modalOpen}     
              placeHolder='Describe yourself'
              label='Bio'
              change={this.handleBio}
                />

ButtonMode

class ButtonMode extends Component {
    constructor(props){
        super(props)
        this.state = {
            input:''
        }
        this.handleInput = this.handleInput.bind(this);
        this.handle = this.handle.bind(this);
    }

    handleInput(val){
        this.setState({input:val})
    };

    handle() {

        this.props.change(this.state.input);
    };

    render(){
        const { classes } = this.props;
        return (
            <div>
                <Button 
                    className={classes.button}
                    onClick={this.props.modalOpen}
                    >Update
                </Button>
                <Modal
                    aria-labelledby="simple-modal-title"
                    aria-describedby="simple-modal-description"
                    open={this.props.open}
                    onClose={this.props.handleClose}
                    >
                    <div className={classes.paper}>
                        <TextField
                            id="filled-textarea"
                            label={this.props.label}
                            placeholder={this.props.placeHolder}
                            multiline
                            className={classes.textField}
                            onChange={(e)=>{this.handleInput(e.target.value)}}
                            rows= '4'
                            />
                        <Button 
                            onClick={this.handle}
                            className={classes.button} 
                            color="secondary">Submit</Button>                  
                  </div>
                </Modal>
            </div>
        )
    }
}

Then I used it like that

 class UserCard extends Component {
    constructor(props){
        super(props);
        this.state = {
          tempPro:'',
          open: false,
          profilePicture:''
        }
        this.modalOpen = this.modalOpen.bind(this);
        this.handleClose = this.handleClose.bind(this);
        this.handlePicture = this.handlePicture.bind(this);
      }



    // componentDidMount(){
    //   const {userId, profilePic} = this.props;
    //   this.setState({profilePicture:profilePic});
    //   // axios.get(`/api/profile/${userId}`).then(res=>{

    //   //   let {profilePic} = res.data[0];
    //   //   this.setState({profilePic})
    //   // })
    // }


    handlePicture(val){
      this.props.changePic(val);
      this.setState({open:false});
    };

    handleBio(val){

      this.setState({open:false});
    };

    handleClose(){
        this.setState({open: false});
    };
    modalOpen(){
      this.setState({open:true});
    };

    render() {
      const { classes } = this.props;
      const {stories} = this.props;
      let storyShow = stories.map((story,id) => {
        return(
          <div value={story.story_id}>
              <h3>{story.title}</h3>
              <ul className={classes.background}>
                <li>{story.description}</li>
                <li>{story.is_complete}</li>
              </ul>  
          </div>
        )
      });

      return (  
      <div className={classes.rootD}>
        <Grid container>
          <Grid className={classes.itemFix} >
          <Card className={classes.card}>
           <CardMedia
            className={classes.media}
            image={this.props.proPic}
            title={this.props.userName}
            />
            <div>
            <ButtonMode 
                  open={this.state.open}
                  handleClose={this.handleClose}
                  modalOpen={this.modalOpen}    
                  placeHolder="New picture url"
                  change={this.handlePicture}
                  label='URL'
                />
            </div>       
          <CardHeader
            className={classes.titles}
            title={this.props.userName}
            subheader="Somewhere"
            />
            <CardHeader className={classes.titles} title='Bio' />
              <CardContent className={classes.background}>
                <Typography className={classes.bio} paragraph>
                  {this.props.bio}
                </Typography>
              </CardContent> 
              <div>
                <ButtonMode 
                  open={this.state.open}
                  handleClose={this.handleClose}
                  modalOpen={this.modalOpen}     
                  placeHolder='Describe you how you want'
                  label='Bio'
                  change={this.handleBio}
                    />
              </div>
          </Card>
          </Grid>
          <Grid className={classes.itemFixT}>
            <Card className={classes.card}>
            <CardContent>
                <CardHeader 
                  className={classes.titles}
                  title='Works'/>
                <Typography paragraph>
                  <ul>
                    {storyShow}
                  </ul>
                </Typography>
              </CardContent>
            </Card>
          </Grid>
          </Grid>
      </div>
      );
    }
  }

  UserCard.propTypes = {
    classes: PropTypes.object.isRequired,
  };
  function mapStateToProps(state){
    const {userId, profilePic} = state;
    return {
      userId,
      profilePic      
    }
  }

  export default connect(mapStateToProps,{})(withStyles(styles)(UserCard));

Upvotes: 6

Views: 8610

Answers (3)

Kaya Toast
Kaya Toast

Reputation: 5493

I spent an embarrassingly long time on a similar issue. I tried all sorts of JS debugging and even re-read the entire concept of closure :)

This is was my culprit: <TextField id="filled-textarea" ... />

i.e. the id is static. If we have multiple instances of the same id on one page, we have a problem.

Make id dynamic, e.g. <TextField id={this.props.label} ... />

Upvotes: 3

cameronridderikhoff
cameronridderikhoff

Reputation: 66

I had a similar issue where I was trying to pass different functions to the children components. I had a UploadFile component that contained an <input/> and a <Button/> from material-ui, and I wanted to reuse this component multiple times throughout a page, as the user has multiple files to upload, and in order to save the files, I needed callback functions in the main page.

What I had to do, was give each child component <UploadFile/> in my case, and <ButtonMode/> in your case, a unique id passed in as a prop, since otherwise, the top level page cannot tell each reference to the child component apart from any others.

The code of the child component:

function UploadFile({ value, handleFile }) {
const classes = useStyles();

return (
<>
  <input
    accept=".tsv,.fa,.fasta"
    className={classes.input}
    id={value}
    type="file"
    style={{ display: 'none' }}
    onChange={e => handleFile(e.target.files[0])}
  />
  <label htmlFor={value}>
    <Button
      variant="contained"
      color='default'
      component="span"
      startIcon={<CloudUploadIcon />}
      className={classes.button}>
      Upload
    </Button>
  </label>
</>
);
}

The usage of this component in the parent (handleFile is the function I am passing in and is defined above in the parent component):

<UploadFile value='prosite' handleFile={handlePrositeFile} />
<UploadFile value='pfam' handleFile={handlePfamFile} />

Upvotes: 5

graveltrunk
graveltrunk

Reputation: 75

I was using the same state for both modals and in each instance of handleOpen() it was only ever opening the last instance of modal in the script.

Upvotes: 0

Related Questions