2015-05-01 10 views
11

Ho scritto questo codice e attualmente sto litigando con un bug in un evento onClick. Ho due eventi, l'evento onClick sull'elemento figlio e l'evento onChange sull'elemento padre di livello superiore.ReactJS: "this.props" non è una funzione quando il componente figlio chiama padre

Il comportamento previsto dovrebbe essere quello di modificare la variabile activeAccount attualmente detenuta nel componente Container. Per fare ciò, ho aggiunto un gestore onClick sul componente AccountRow che dovrebbe quindi chiamare la funzione onChange del padre di livello superiore.

Tuttavia, la linea

'this.props.onChange(this.props.account)', 

in

handleClick: function(e) { 
     this.props.onChange(this.props.account); 
    },  

lo scopo di richiamare la funzione genitore con parametro 'this.props.account',

mi da questo errore:

Uncaught TypeError: this.props.onChange is not a function. 

inizialmente ho pensato che questo era un problema di scoping, ma ho aggiunto

{...this.props} 

su ogni bambino e bambina nidificato all'interno del genitore Container componente. Quindi, tutti gli oggetti di scena dovrebbero essere stati propagati al componente AccountRow. Tuttavia, il problema rimane ancora.

var ACCOUNTS = [ 
    {name: 'cash on hand'}, 
    {name: 'savings account'}, 
    {name: 'shared account'}, 
    {name: 'my second wallet'} 
]; 

var TRANSACTIONS = [ 
    {date: '', name: 'Bananas', amount: 6, category: 'Groceries', account: 'cash on hand'}, 
    {date: '', name: 'Apples', amount: 2.50, category: 'Groceries', account: 'cash on hand'}, 
    {date: '', name: 'Cash withdrawal', amount: 250, account: 'savings account'} 
]; 

var AccountRow = React.createClass({ 

    handleClick: function(e) { 
     this.props.onChange(this.props.account); 
    }, 

    render: function() { 
     return (
      <li onClick = {this.handleClick}> {this.props.account}</li> 
     )} 
}); 

var AccountList = React.createClass({ 

    render: function() { 

     var rows = []; 
     this.props.accounts.map(function(each_account) { 
      rows.push(
       <AccountRow 
        account = {each_account.name} 
        key = {each_account.name} 
        {...this.props}  
       />); 
         }) 
    return (
     <ul> 
     {rows} 
     </ul> 
    ) 
    } 
}); 


var NavBar = React.createClass({ 

    render: function() { 
     return (
      <div id = 'account-tabs'> 
       <h2> Accounts </h2> 
       <AccountList 
        accounts = {this.props.accounts} 
        {...this.props} 
       /> 
      </div> 
     ) 
    } 
}); 

var TransactionRow = React.createClass({ 
    render: function(){ 
     var trans = this.props.transaction; 

     return (
      <tr> 
      <td>{trans.date}</td> 
      <td>{trans.name}</td> 
      <td>{trans.amount}</td> 
      <td>{trans.account}</td> 
      <td><a href = ''>edit</a></td> 
      </tr> 
     ) 
    } 
}); 

var TransactionList = React.createClass ({      
    render: function() { 

     var activeaccount = this.props.activeAccount; 

     var rows = []; 
     this.props.transactions.map(function(each_transaction) { 
      if (each_transaction.account == activeaccount) { 

       /* Very strange behaviour 
       if (each_transaction account == this.props.activeAccount) 
       DOES NOT WORK, I do not know why this is the case 
       */ 

       rows.push(<TransactionRow transaction = {each_transaction} key = {each_transaction.name} />); 
      } 
      else { 
       /*console.log(each_transaction.account);*/ 
      }  
     }) 
     return (
      <tbody> 
      {rows} 
      </tbody> 
     ) 
    } 
}); 

var TransactionsTable = React.createClass({ 
    render: function() { 

     return (
      <div id = 'recent-transactions'> 
      <h2>Recent Transactions for {this.props.activeAccount}</h2> 
      <table> 
       <tr> 
        <th>date</th> 
        <th>name</th> 
        <th>amount</th> 
        <th>account</th> 
        <th>edit </th> 
       </tr> 
       <TransactionList 
        transactions = {this.props.transactions} 
        activeAccount = {this.props.activeAccount} 
      /> 
      </table> 
      </div> 
     ) 
    } 
}); 

var TransactionForm = React.createClass({ 
    render: function() { 
     return (
      <div> 
      <h2>Add Transaction </h2> 
      <p>Transaction name</p> 
      <input type = 'text' /> 
      <p>Price</p> 
      <input type = 'number'/> 
      <p>Category (optional) </p> 
      <input type = 'text' /> 
      </div> 
     ) 
    } 
}); 


var ButtonMenu = React.createClass ({ 
    render: function() { 
     return (
      <div> 
      <button>Add Transaction</button> 
      </div> 
     ) 
    } 
}); 

var Container = React.createClass({ 

    getInitialState: function(){ 
     return { 
      activeAccount: ACCOUNTS[0].name 
     }; 
    }, 

    setActiveAccount: function(dat) { 
     this.setState({ 
      activeAccount: dat 
     }); 
    }, 

    render: function() { 
     return (
      <div id = 'wrapper'> 
      <NavBar 
       accounts = {ACCOUNTS} 
       activeAccount = {this.state.activeAccount} 
       onChange = {this.setActiveAccount} 
       {...this.props} 
      /> 
      <TransactionsTable 
       transactions = {TRANSACTIONS} 
       activeAccount = {this.state.activeAccount} 
      /> 
      <TransactionForm /> 
      <ButtonMenu /> 
      </div> 
     ); 
    } 
}); 


React.render(<Container />, document.body); 

Grazie per il vostro tempo.

+1

Sei sicuro 'this.props' in realtà ha una funzione di callback' onChange'? – skyline75489

+0

Credo di averlo nel componente , nella funzione di rendering del contenitore. –

risposta

9

Il problema è causato da map funzione, si dovrebbe passare a this per thisArg al momento della chiamata map:

this.props.accounts.map(function(each_account) { 
    rows.push(
    <AccountRow 
     account = {each_account.name} 
     key = {each_account.name} 
     {...this.props}  
    />); 
}, this); 

Tuttavia, questo farà sì che AccountRow avere variabili ridondanti come accounts e activeAccount. Penso che si dovrebbe prendere in considerazione il trasferimento solo la funzione onChange:

<AccountRow 
    account = {each_account.name} 
    key = {each_account.name} 
    onChange = {this.props.onChange} 
/> 
+0

Buona cattura! Come faccio a passare la funzione 'onChange' lungo la catena? Faccio a creare una nuova funzione, come ');' che poi chiama il livello superiore 'onChange' funzione? O c'è un modo per passare il metodo 'onChange' tramite oggetti di scena? –

+0

Ho modificato la risposta. Spero che sia d'aiuto. – skyline75489

+0

Grazie per il vostro aiuto! Puoi spiegare come la funzione ', this' aggiunta alla fine della funzione' rows.push' aiuta a risolvere questo problema? –