I want to get a ref to the component represented by the element i create, but cannot get it to work. I tried this:
var comp = React.createElement(
MyComp,
{
props: myprops,
ref: "mycomp"
}
);
But this doesn't work. How do i set a ref on it so the parent can call this.refs.mycomp.someMethod()?
https://facebook.github.io/react/docs/top-level-api.html#react.createelement
ReactElement createElement(
string/ReactClass type,
[object props],
[children ...]
)
The second parameter of the function is an optional props object for the component. Unless you want to refer to the props in the component as props.props you can splat the myProps object:
var comp = React.createElement(MyComp, { ...myprops, ref: "mycomp" });
class MyComp extends React.Component {
constructor(props) {
super(props);
this.initialValue = props.initialValue;
this.state = { value: this.initialValue };
this.increment = this.increment.bind(this);
this.reset = this.reset.bind(this);
}
increment() {
this.setState({ value: this.state.value + 1 });
}
reset() {
this.setState({ value: this.initialValue });
}
render() {
return (
<div className="child">
<h1>Counter: {this.state.value}</h1>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.reset = this.reset.bind(this);
}
reset() {
this.refs.mycomp.reset();
}
render() {
const myProps = { initialValue: 1 };
const Comp = React.createElement(MyComp, { ...myProps, ref: "mycomp" });
return (
<div className="parent">
{Comp}
<button onClick={this.reset}>Reset</button> Calls this.refs.mycomp.reset
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
.parent {
background-color: #555;
color: #FFF;
padding: 10px;
}
.child {
background-color: #888;
padding: 10px;
}
h1 {
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
As of react v16.3, this.refs has been discouraged. One of the popular ways of doing it would be,
const Comp = React.createElement(
MyComp,
{
props: myprops,
ref: ref => this.mycomp = ref
}
);
Related
Every time I click an option of size and click add to cart I would like to add the data of the selected object to this array cart. This currently works kinda but only one object can be added and when you try to do it again the old data disappears and is replaced with the new object.
I would like to keep odd objects in the array and add new objects too. How do I go about doing this?
index.js
export class App extends Component {
constructor(props) {
super(props);
this.state = {
evenSelected: null
};
}
handleSelectL1 = i => {
this.setState({
evenSelected: i,
oldSelected: null
});
};
render() {
const product = [
{
name: " size one",
price: 1
},
{
name: "size two",
price: 2
},
,
{
name: "size three",
price: 3
}
];
const cart = [];
const addCart = function() {
cart.push(product[evenIndex]);
if (cart.length > 0) {
}
};
console.log("cart", cart);
const evenIndex = this.state.evenSelected;
const priceShown = product[evenIndex] && product[evenIndex].price;
return (
<div>
<Child
product={product}
handleSelectL1={this.handleSelectL1}
evenIndex={evenIndex}
/>
<h2>Price:{priceShown} </h2>
<button onClick={addCart}>Add to cart</button>
</div>
);
}
}
child.js
export class Child extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
const { product, evenIndex } = this.props;
return (
<div>
{product.map((p, i) => {
return (
<div
key={p.id}
className={evenIndex === i ? "selectedRBox" : "selectorRBox"}
onClick={() => this.props.handleSelectL1(i)}
>
<h1 className="selectorTextL">{p.name}</h1>
</div>
);
})}
</div>
);
}
}
Here is my code on sandbox: https://codesandbox.io/s/14vyy31nlj
I've just modified your code to make it work. Here is the complete code. You need cart to be part of the state, so it does not initialize in each render, and to make the component render again when you add an element.
Remove the function to make it a method of the class:
addToCart() {
const selectedProduct = products[this.state.evenSelected];
this.setState({
cart: [...this.state.cart, selectedProduct]
});
}
And call it on render:
render() {
console.log("cart", this.state.cart);
const evenIndex = this.state.evenSelected;
const priceShown = products[evenIndex] && products[evenIndex].price;
return (
<div>
<Child
product={products}
handleSelectL1={this.handleSelectL1}
evenIndex={evenIndex}
/>
<h2>Price:{priceShown} </h2>
<button onClick={this.addToCart.bind(this)}>Add to cart</button>
</div>
);
}
}
Check that I have binded on render, which can bring performance issues in some cases. You should check this
Update
As devserkan made me notice (Thanks!), when you use the previous state to define the new state (for example adding an element to an array), it is better to use the updater function instead of passing the new object to merge:
this.setState(prevState => ({
cart: [...prevState.cart, products[selectedProduct]],
}));
For more info check the official docs.
I don't quite understand what are you trying to but with a little change here it is. I've moved product out of the components like a static variable. Also, I've changed the addCart method, set the state there without mutating the original one and keeping the old objects.
const product = [
{
name: " size one",
price: 1
},
{
name: "size two",
price: 2
},
{
name: "size three",
price: 3
}
];
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
evenSelected: null,
cart: [],
};
}
handleSelectL1 = i => {
this.setState({
evenSelected: i,
oldSelected: null
});
};
addCart = () => {
const evenIndex = this.state.evenSelected;
this.setState( prevState => ({
cart: [ ...prevState.cart, product[evenIndex] ],
}))
};
render() {
console.log(this.state.cart);
const evenIndex = this.state.evenSelected;
const priceShown = product[evenIndex] && product[evenIndex].price;
return (
<div>
<Child
product={product}
handleSelectL1={this.handleSelectL1}
evenIndex={evenIndex}
/>
<h2>Price:{priceShown} </h2>
<button onClick={this.addCart}>Add to cart</button>
</div>
);
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
const { product, evenIndex } = this.props;
return (
<div>
{product.map((p, i) => {
return (
<div
key={p.id}
className={evenIndex === i ? "selectedRBox" : "selectorRBox"}
onClick={() => this.props.handleSelectL1(i)}
>
<h1 className="selectorTextL">{p.name}</h1>
</div>
);
})}
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
.selectorRBox {
width: 260px;
height: 29.5px;
border: 1px solid #727272;
margin-top: 18px;
}
.selectedRBox {
width: 254px;
height: 29.5px;
margin-top: 14px;
border: 4px solid pink;
}
.selectorTextL {
font-family: "Shree Devanagari 714";
color: #727272;
cursor: pointer;
font-size: 18px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
In Draft-JS, I would like a basic custom block, rendering an <h1> element. I would like to add some text before my h1, that the user cannot edit. The text is here to inform people that this block is for Title. So I would like to add "TITLE" in front of the block that is not editable.
What is the best way to achieve this in Draft JS?
You can achieve your aim by applying contentEditable={false} and readOnly property on the node that should be read-only:
class MyCustomBlock extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="my-custom-block">
<h1
contentEditable={false} // <== !!!
readOnly // <== !!!
>
Not editable title
</h1>
<div className="editable-area">
<EditorBlock {...this.props} />
</div>
</div>
);
}
}
Check working demo in the hidden snippet below:
const {Editor, CharacterMetadata, DefaultDraftBlockRenderMap, ContentBlock, EditorBlock, genKey, ContentState, EditorState} = Draft;
const { List, Map, Repeat } = Immutable;
class MyCustomBlock extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="my-custom-block">
<h1
contentEditable={false}
readOnly
>
Not editable title
</h1>
<div className="editable-area">
<EditorBlock {...this.props} />
</div>
</div>
);
}
}
function blockRendererFn(contentBlock) {
const type = contentBlock.getType();
if (type === 'MyCustomBlock') {
return {
component: MyCustomBlock,
props: {}
};
}
}
const RenderMap = new Map({
MyCustomBlock: {
element: 'div',
}
}).merge(DefaultDraftBlockRenderMap);
const extendedBlockRenderMap = Draft.DefaultDraftBlockRenderMap.merge(RenderMap);
class Container extends React.Component {
constructor(props) {
super(props);
this.state = {
editorState: EditorState.createEmpty()
};
}
_handleChange = (editorState) => {
this.setState({ editorState });
}
_onAddCustomBlock = () => {
const selection = this.state.editorState.getSelection();
this._handleChange(addNewBlockAt(
this.state.editorState,
selection.getAnchorKey(),
'MyCustomBlock'
))
}
render() {
return (
<div>
<div className="container-root">
<Editor
placeholder="Type"
blockRenderMap={extendedBlockRenderMap}
blockRendererFn={blockRendererFn}
editorState={this.state.editorState}
onChange={this._handleChange}
/>
</div>
<button onClick={this._onAddCustomBlock}>
ADD CUSTOM BLOCK
</button>
</div>
);
}
}
ReactDOM.render(<Container />, document.getElementById('react-root'));
const addNewBlockAt = (
editorState,
pivotBlockKey,
newBlockType = 'unstyled',
initialData = new Map({})
) => {
const content = editorState.getCurrentContent();
const blockMap = content.getBlockMap();
const block = blockMap.get(pivotBlockKey);
if (!block) {
throw new Error(`The pivot key - ${ pivotBlockKey } is not present in blockMap.`);
}
const blocksBefore = blockMap.toSeq().takeUntil((v) => (v === block));
const blocksAfter = blockMap.toSeq().skipUntil((v) => (v === block)).rest();
const newBlockKey = genKey();
const newBlock = new ContentBlock({
key: newBlockKey,
type: newBlockType,
text: '',
characterList: new List(),
depth: 0,
data: initialData,
});
const newBlockMap = blocksBefore.concat(
[[pivotBlockKey, block], [newBlockKey, newBlock]],
blocksAfter
).toOrderedMap();
const selection = editorState.getSelection();
const newContent = content.merge({
blockMap: newBlockMap,
selectionBefore: selection,
selectionAfter: selection.merge({
anchorKey: newBlockKey,
anchorOffset: 0,
focusKey: newBlockKey,
focusOffset: 0,
isBackward: false,
}),
});
return EditorState.push(editorState, newContent, 'split-block');
};
body {
font-family: Helvetica, sans-serif;
}
.container-root {
border: 1px solid black;
padding: 5px;
margin: 5px;
}
.my-custom-block {
background-color: cadetblue;
margin: 15px 0;
font-size: 16px;
position: relative;
}
.editable-area {
background-color: lightblue;
height: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.1/immutable.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.0/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/draft-js/0.10.0/Draft.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/draft-js/0.7.0/Draft.css" rel="stylesheet"/>
<div id="react-root"></div>
I'm beginner on react and i've written the code below:
class Note extends React.Component {
constructor(props) {
super(props);
this.state = {editing: false};
this.edit = this.edit.bind(this);
this.save = this.save.bind(this);
}
edit() {
// alert('edit');
this.setState({editing: !this.state.editing});
}
save() {
this.props.onChange(this.refs.newVal.value, this.props.id);
this.setState({editing: !this.state.editing});
// console.log('save is over');
}
renderForm() {
return (
<div className="note">
<textarea ref="newVal"></textarea>
<button onClick={this.save}>SAVE</button>
</div>
);
}
renderDisplay() {
return (
<div className="note">
<p>{this.props.children}</p>
<span>
<button onClick={this.edit}>EDIT</button>
<button onClick={this.remove}>X</button>
</span>
</div>
);
}
render() {
console.log(this.state.editing);
return (this.state.editing) ? this.renderForm()
: this.renderDisplay()
}
}
class Board extends React.Component {
constructor(props){
super(props);
this.state = {
notes: []
};
this.update = this.update.bind(this);
this.eachNote = this.eachNote.bind(this);
this.add = this.add.bind(this);
}
nextId() {
this.uniqeId = this.uniqeId || 0;
return this.uniqeId++;
}
add(text) {
let notes = [
...this.state.notes,
{
id: this.nextId(),
note: text
}
];
this.setState({notes});
}
update(newText, id) {
let notes = this.state.notes.map(
note => (note.id !== id) ?
note :
{
id: id,
note: newText
}
);
this.setState({notes})
}
eachNote(note) {
return (<Note key={note.id}
id={note.id}
onChange={this.update}>
{note.note}
</Note>)
}
render() {
return (<div className='board'>
{this.state.notes.map(this.eachNote)}
<button onClick={() => this.add()}>+</button>
</div>)
}
}
ReactDOM.render(<Board />,
document.getElementById('root'));
In render(), onClick event has a function, that is, if used in this way: {this.add} the following error is created:
Uncaught Error: Objects are not valid as a React child (found: object with keys {dispatchConfig, _targetInst, nativeEvent, type, target, currentTarget, eventPhase, bubbles, cancelable, timeStamp, defaultPrevented, isTrusted, view, detail, ...})
Why? while in the eachNote() method this command is used:
onChange={this.update}
And there was no error.
Someone can tell me the reason? thanks.
The problem is that in the add function you are taking an argument text and setting it in the state so when you call onClick={() => this.add()}, you are not passing any argument to add function and hence in its definition text is undefned and hence state note is set as undefined.
However if you directly call it like onClick={this.add} , the add function receives the event object as a parameter and hence it sets state note to be an event object which you are using to render
onClick={this.add} will pass the click event to this.add.
So what you need to do is either:
onClick={e => this.add('some text')} or similar.
If you want to onClick={this.add} you have to ensure that your add method is: add(event) { ... } instead.
The <Note /> component does not contain a render() method to return anything. Add a render() method and return something.
class Note extends React.Component {
constructor(props) {
super(props);
this.state = {editing: false};
this.edit = this.edit.bind(this);
}
edit() {
// alert('edit');
this.setState({editing: !this.state.editing});
}
render() {
return (
<div>Render something</div>
)
}
}
class Board extends React.Component {
constructor(props){
super(props);
this.state = {
notes: []
};
this.update = this.update.bind(this);
this.eachNote = this.eachNote.bind(this);
this.add = this.add.bind(this);
}
nextId() {
this.uniqeId = this.uniqeId || 0;
return this.uniqeId++;
}
add(text) {
let notes = [
...this.state.notes,
{
id: this.nextId(),
note: text
}
];
this.setState({notes});
}
update(newText, id) {
let notes = this.state.notes.map(
note => (note.id !== id) ?
note :
{
id: id,
note: newText
}
);
this.setState({notes})
}
eachNote(note) {
return (<Note key={note.id}
id={note.id}
onChange={this.update}>
{note.note}
</Note>)
}
render() {
return (<div className='board'>
{this.state.notes.map(this.eachNote)}
<button onClick={() => this.add()}>+</button>
</div>)
}
}
ReactDOM.render(<Board />,
document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<div id="root"></div>
If I have say the following simple components:
const CompOne = (props) => {
return (
<div className="compOne">
{props.children}
</div>
);
};
const CompTwo = (props) => {
return (
<div className="compTwo">
{props.children}
</div>
);
};
const CompThree = (props) => {
return (
<div className="compThree">
{props.content}
</div>
);
};
Now during run time, after making an AJAX request the client receives information that gives the order in which components need to wrap into one another. The result of that AJAX request would look something like this:
let renderMap = [
{ component: CompOne, props: {} },
{ component: CompTwo, props: {} },
{ component: CompThree, props: { content: "hi there" } }
];
So the composition should flow by iterating through the array and composing one component into the next. e.g: CompOne(CompTwo(CompThree)))
Two important things to note when I tried creating a wrapping HOC to fix this issue:
Edit: Important detail I forgot to mention in the original post
1) The number of components to wrap will not be consistent. At times it could be 3, but other times as many as 4, 5, 6 components needed to wrap into each other
2) The order could be different each time
<CompOne>
<CompTwo>
<CompThree content="hi there">
</CompThree>
</CompTwo>
</CompOne>
So my resulting HTML would be:
<div className="compOne">
<div className="compTwo">
<div className="compThree">
hi there
</div>
</div>
</div>
I've tried various things but I can't get it to work once I start getting past just wrapping two components. Is this something I can even do in React?
Like the link that Arup Rakshit posted in the comments showed, you can use components that are stored in a variable - with JSX - as long as they are capitalized:
// in render()
const MyComp = props.someVariableContainingAComponent;
return <MyComp />;
With that in mind, one approach to your problem would be to iterate through all your components, starting with inner one, and then taking the each of the next to use as a wrapper for the previous one. Given the shape of your test data renderMap, and using Array.protype.reduce for the iteration, it could look something like this:
renderComponents(renderMap) {
const Component = renderMap
.reverse()
.reduce( (ComponentSoFar, {component, props}) => {
const Outer = component;
return () => (<Outer {...props} ><ComponentSoFar /></Outer>);
}, props => null ); // initial value, just a "blank" component
return ( <Component /> );
}
I have included a demo showing how both different number of components and varying order of nesting can be handled with this approach.
const CompOne = (props) => (
<div className="comp compOne"><p>One:</p>{ props.content || props.children } </div>);
const CompTwo = (props) => (
<div className="comp compTwo"><p>Two:</p> { props.content || props.children }</div>);
const CompThree = (props) => (
<div className="comp compThree"><p>Three:</p> { props.content || props.children }</div>);
const CompFour = (props) => (
<div className="comp compFour"><p>Four:</p> { props.content || props.children }</div>);
const CompFive = (props) => (
<div className="comp compFive"><p>Five:</p> { props.content || props.children }</div>);
const renderMap1 = [
{ component: CompOne, props: {} },
{ component: CompTwo, props: {} },
{ component: CompThree, props: {} },
{ component: CompFour, props: {} },
{ component: CompFive, props: { content: "hi there" } }
];
const renderMap2 = [].concat(renderMap1.slice(1,4).reverse(), renderMap1.slice(4))
const renderMap3 = renderMap2.slice(1);
class App extends React.Component {
constructor(props) {
super(props);
}
renderComponents(renderMap) {
const Component = renderMap
.reverse()
.reduce( (ComponentSoFar, {component, props}) => {
const Outer = component;
return () => (<Outer {...props} ><ComponentSoFar /></Outer>);
}, props => null ); // initial value, just a "blank" component
return ( <Component /> );
}
render() {
return ( <div>
{ this.renderComponents(renderMap1) }
{ this.renderComponents(renderMap2) }
{ this.renderComponents(renderMap3) }
</div> );
}
};
ReactDOM.render(<App />, document.getElementById('root'));
.comp {
border: 5px solid green;
padding: 5px;
margin: 5px;
display: inline-block;
}
.compOne { border-color: red;}
.compTwo { border-color: green;}
.compThree { border-color: blue;}
.compFour { border-color: black;}
.compFive { border-color: teal;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Edit: New info was added to the question. Given that info, this approach doesn't work.
You can probably solve it using a Higher-order component (HOC), something like this:
const superWrapped = (Outer) => (Middle) => (Inner) => props => {
return (
<Outer>
<Middle>
<Inner content={props.content} />
</Middle>
</Outer>
)
};
Where you would later use it like this:
render() {
const SuperWrapped =
superWrapped(CompOne)(CompThree)(CompTwo); // any order is fine!
return (<SuperWrapped content="Something here.." /> );
}
Some minor adjustments on your components would be necessary for this to work. I've included a working demo below:
const superWrapped = (Outer) => (Middle) => (Inner) => props => {
return (
<Outer>
<Middle>
<Inner content={props.content} />
</Middle>
</Outer>)
};
const CompOne = (props) => {
return (
<div className="compOne">
<p>One:</p>
{props.children || props.content}
</div>
);
};
const CompTwo = (props) => {
return (
<div className="compTwo">
<p>Two:</p>
{props.children || props.content}
</div>
);
};
const CompThree = (props) => {
return (
<div className="compThree">
<p>Three:</p>
{props.children || props.content}
</div>
);
};
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
const components = getComponentOrder();
const SuperWrapped1 =
superWrapped(components[0])(components[1])(components[2]);
const SuperWrapped2 =
superWrapped(components[2])(components[1])(components[0]);
return (
<div>
<SuperWrapped1 content="Hello, world!" />
<SuperWrapped2 content="Goodbye, world!" />
</div>
);
}
}
const getComponentOrder = () => {
return Math.random() < 0.5 ?
[CompOne, CompTwo, CompThree] :
Math.random() < 0.5 ?
[CompThree, CompOne, CompTwo] :
[CompTwo, CompOne, CompThree]
};
ReactDOM.render(<App />, document.getElementById('root'));
.compOne {
border: 5px solid red;
padding: 5px;
margin: 5px;
display: inline-block;
}
.compTwo {
border: 5px solid green;
padding: 5px;
margin: 5px;
display: inline-block;
}
.compThree {
border: 5px solid blue;
padding: 5px;
margin: 5px;
display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
The easiest way to achieve this is to use a recursive function:
let renderMap = [
{ component: CompOne, props: {} },
{ component: CompTwo, props: {} },
{ component: CompThree, props: { content: "hi there" } }
];
function App() {
let index = 0;
let structure = "Hi There!"
function packing() {
if (index === renderMap?.length) return;
const Comp = renderMap[index].component
structure = <Comp>{structure}</Comp>;
index += 1;
packing();
}
packing();
return <>{structure}</>
}
For some reason when I try to bind a function to a prop in my React component, it is coming up with
TypeError: Can't add property onSearch, object is not extensible.
I am not too familiar with what this means or why it is appearing, I think it may be to do with the es6 bindings which I am still finding my way around on.
Here are the two most relevant components.
Searchbox
import React from 'react';
import SearchForm from 'SearchForm';
import searchDisplay from 'searchDisplay';
import googleRequests from 'googleRequests';
class SearchBox extends React.Component {
constructor() {
super();
this.state = {
searchResults: []
}
this.handleSearch = this.handleSearch.bind(this);
}
handleSearch(searchTerm) {
googleRequests.search(searchTerm).then((response) => {
console.log(response.items);
this.extrapolateResults(response.items);
}), ((error) =>{
console.log(error)
});
}
//pull relevant data out of api call
extrapolateResults(arr) {
function Book(objInArr) {
this.link = objInArr.selfLink;
this.bookTitle = objInArr.volumeInfo.title;
this.author = objInArr.volumeInfo.authors;
this.bookDescription = objInArr.volumeInfo.description;
this.thumbnail = function() {
if (objInArr.volumeInfo.hasOwnProperty('imageLinks')){
return objInArr.volumeInfo.imageLinks.smallThumbnail
}
else {
return "No Thumbnail Available";
}
};
this.thumbnailPic = this.thumbnail();
}
//push extrapolated data into array
var finalRes = [];
var initRes = arr;
initRes.forEach(function (objInArr) {
var obj = new Book(objInArr);
finalRes.push(obj);
})
this.setState({
searchResults: finalRes
})
console.log(finalRes, this.state.searchResults)
}
render() {
var res = this.state.searchResults;
function renderResults() {
if (res.length !== 0) {
return (<SearchDisplay content={res} />)
}
else {
return;
}
}
var style = {
border: '1px solid black',
height: '80%',
width: '83%'
}
return (
<div style={style}>
<SearchForm onSearch={this.handleSearch}> </SearchForm>
</div>)
}
};
export default SearchBox;
Searchform
import React from 'react';
class SearchForm extends React.Component {
constructor(props) {
super(props);
this.onFormSubmit = this.onFormSubmit.bind(this);
this.props.onSearch = props;
}
onFormSubmit(e) {
e.preventDefault();
var searchTerm = this.refs.searchTerm.value;
if (searchTerm.length > 0) {
this.refs.searchTerm.value = '';
this.props.onSearch(searchTerm);
}
}
render() {
var style = {
border: '1px solid black',
float: 'left',
height: '100%',
width: '30%'
}
return(
<div style={style} className="container">
<form onSubmit={this.onFormSubmit}>
<input type="text" placeholder="search name here" ref="searchTerm"/>
<input type="submit" className="button" value="Get Book"/>
</form>
</div>
);
}
};
export default SearchForm;
I have a feeling I am missing something very simple but after googling this issue for a while I still can't figure out what it is...
remove this.props.onSearch = props;... not sure what you wanted to do there on that line
change handleSearch function definition to handleSearch = () => {} fat arrow function
it will work fine in searchbox file.