Display search results on keypress - reactjs

I am tryng to display search results for each key press on my input:
getMovies(e){
axios.get(`http://www.omdbapi.com/?t=${e.target.value}`)
.then((response) => {
this.setState({ movies: response.data });
})
.catch((error) => {
console.log(error);
});
}
render() {
return (
<div className="container">
<SearchForm onkeydown={this.getMovies} />
<MovieList movies={this.state.movies}/>
</div>
);
}
}
In my search form I bind my function to the FormControl onChange:
export default class SearchForm extends React.Component{
render(){
return(
<Row>
<Col md={6} >
<h2>Custom search field</h2>
<div className="custom-search-input">
<Col md={12} className="input-group" >
<FormControl
type="text"
bsSize="lg"
value={this.props.val}
placeholder="Enter text"
onChange={this.props.onkeydown.bind(this)}
/>
<span className="input-group-btn">
<button className="btn btn-lg" type="button">
<i className="glyphicon glyphicon-search"></i>
</button>
</span>
</Col>
</div>
</Col>
</Row>)
}
}
My movielist component:
export default class MovieList extends React.Component{
render(){
var userNodes = this.props.movies.map(function(movie){
return (
<tr key={movie.id}>
<td>{movie.Year}</td>
<td >{movie.Title}</td>
<td >{movie.Released}</td>
</tr>
)
});
return (
<div>
<Table responsive>
<thead>
<tr>
<th>id</th>
<th>Year</th>
<th>Title</th>
<th>Released</th>
</tr>
</thead>
<tbody>
{userNodes}
</tbody>
</Table>
</div>
);
}
}
I can get the response on the network panel, but the state is not updating to display the MovieList component.
How can I update the state and display it in my MovieList ?

I've checked that API and seems it returns an object, not an array.
Update 1
You can convert your component in something like:
export default class MovieList extends React.Component{
render(){
const { movie } = this.props;
return (
<div>
<Table responsive>
<thead>
<tr>
<th>Id</th>
<th>Year</th>
<th>Title</th>
<th>Released</th>
</tr>
</thead>
<tbody>
<tr>
<td>{movie.imdbID}</td>
<td>{movie.Year}</td>
<td>{movie.Title}</td>
<td>{movie.Released}</td>
</tr>
</tbody>
</Table>
</div>
);
}
}
and use it like:
<MovieList movie={this.state.movies} />
Please notice I'm using movie instead of movies.
Update 2:
You can also (and I would suggest doing this) convert your MovieList into a dumb functional component:
const MovieList = ({ movie }) => (
<div>
<Table responsive>
<thead>
<tr>
<th>Id</th>
<th>Year</th>
<th>Title</th>
<th>Released</th>
</tr>
</thead>
<tbody>
<tr>
<td>{movie.imdbID}</td>
<td>{movie.Year}</td>
<td>{movie.Title}</td>
<td>{movie.Released}</td>
</tr>
</tbody>
</Table>
</div>
)
export default MovieList;

Related

React pass params to the node elements from child component

I need to pass params from child component to the node elements from parent props.
Page component
export default function Categories() { const dispatch = useDispatch();
useEffect(() => {
dispatch(loaderAction(false));
return () => dispatch(loaderAction(true)); }, [dispatch]);
function handleClick(params) {
alert(); }
function handleEditClick(params) {
alert(); }
return (
<div className="categories-body">
<div className="categories-header">
<div className="left">
<h2>{t('[categories]')}</h2>
</div>
<div className="right">
<button className="button orange">{t('[addNewCategory]')}</button>
<LanguageSettings name="categoriesLanguageSetting" />
</div>
</div>
// Table component imported
<Table
action={
<>
//node elements
<button onClick={handleClick}>save</button>
<button onClick={handleEditClick}>edit</button>
</>
}
/>
</div> ); }
TableComponent
export default function Table({action}) {
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>date</th>
<th>key</th>
<th>category</th>
<th>actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>{action}</td> //pass parameter to node props
</tr>
</tbody>
</table>
);
}
I have pass two buttons to the Table component and need to pass for example row id to button onClick
Just pass the function... not the component
const handleClick = id => alert();
<Table action={handleClick} />
function Table({action}) {
return (
<table>
...
<td><button onClick={() => action(`someId`)}>action</button></td>
</table>
);
}
Or if you insist, pass a function component not an element:
actions={({id}) => <button onClick={() => handleClick(id)}>save</button>}
// Usage
function Table({action}) {
const Action = props.action;
return (
<table>
...
<td><Action id="Some id"/></td>
</table>
);
}
in your page component, just pass the actions prop as a render prop function
function handleClick(params) {
console.log(params);
alert();
}
function handleEditClick(params) {
console.log(params);
alert();
}
return (
<Table
action={ (params) => (
<>
<button onClick={() => handleClick(params)}>save</button>
<button onClick={() => handleEditClick(params)}>edit</button>
</>
)
}
/>
)
and in the table component call that function with the desired params,
with this approach, you can extend the render prop function to deliver multiple params
function Table({action}) {
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>date</th>
<th>key</th>
<th>category</th>
<th>actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>{action(23)}</td> // pass params here
</tr>
</tbody>
</table>
);
}

reactjs iterate over a array in functional component

I have a array of employee objects and i'm using that array(passed as state) to build rows in a table(as shown below). I get this error while iterating over the array. when I display the array using {props.employee_data}(the line is commented)
TypeError: Cannot read property 'map' of null
Code:
export default function(props) {
let employees = null
return (
<Table striped bordered hover size="sm">
<thead>
<tr>
<th>Full Name</th>
<th>Gender</th>
<th>Current Annual Salary</th>
<th>2018 Gross Pay Received</th>
<th>2018 Overtime Pay</th>
<th>Department</th>
<th>Department Name</th>
<th>Division</th>
<th>Assignment Category</th>
<th>Employee Position Title</th>
<th>Position Under-filled</th>
<th>Date First Hired</th>
</tr>
</thead>
<tbody>
{/* <tr>
<td>1</td>
<td>Mark</td>
<td>Otto</td>
<td>#mdo</td>
</tr>
*/}
{/* {props.employee_data} */}
{props.employee_data.map((employee)=>{
return <tr>employee</tr>
}
)}
</tbody>
</Table>
)
}
parent component
class App extends Component {
constructor(props){
super(props)
this.state = {
employee_data: null,
column_names: null
}
}
componentDidMount = () => {
this.fetchData();
}
fetchData = async () => {
fetch(county_data).
then(response => response.json()).
then(response => {
console.log(response.data)
let column_names = response.meta.view.columns.map((item)=>{
return item['name']
})
// console.log(JSON.stringify(response.data))
this.setState({
employee_data: response.data,
column_names: column_names
})
})
}
render() {
return (
<div className="App">
<Container>
<Row>
<Col lg={4}>1 of 3</Col>
<Col lg={4}>2 of 3</Col>
<Col lg={4}>2 of 3</Col>
</Row>
</Container>
<br></br>
<br></br>
<DataTable employee_data={this.state.employee_data}/>
</div>
);
}
}
export default App;
Initially the employee_data is null, it is not until your fetch, the employee data is an array.
map is a function on the array's prototype, hence it will not be available on the initial employee_data which is null. Only after the fetch, the employee data is an array and the map function is available.
To get around this, you can initially declare employee_data as an empty array.
this.state = {
employee_data: [],
... other state data,
}
you can do th
export default function(props) {
let employees = props.employee_data?props.employee_data:[];
return (
<Table striped bordered hover size="sm">
<thead>
<tr>
<th>Full Name</th>
<th>Gender</th>
<th>Current Annual Salary</th>
<th>2018 Gross Pay Received</th>
<th>2018 Overtime Pay</th>
<th>Department</th>
<th>Department Name</th>
<th>Division</th>
<th>Assignment Category</th>
<th>Employee Position Title</th>
<th>Position Under-filled</th>
<th>Date First Hired</th>
</tr>
</thead>
<tbody>
{/* <tr>
<td>1</td>
<td>Mark</td>
<td>Otto</td>
<td>#mdo</td>
</tr>
*/}
{/* {props.employee_data} */}
{employees.map((employee)=>{
return <tr>employee</tr>
}
)}
</tbody>
</Table>
)
}

Rendering table's data header in reactjs

I could render the herader's table users through for exemple: {user[name]}. But I couldn't render the header itself who is the [name] for example rendering: name of Leanne Graham. Can anyone help me with this?
import React from 'react'
const Users= ({ users}) => {
return (
<div>
{users.map((user,index) => (
<div key={index}>
<div className="container smcontainer d-flex justify-content-start">
<div className="row">
<div className="col-md-12">
<table className="table table-striped">
<thead>
</thead>
<tbody>
<tr>
<td className=""> {user['id']} </td>
<td className=""> {user['name']} </td>
<td className=""> {user['username']} </td>
<td className=""> {user['email']} </td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
))}
</div>
)}
export default Products
This is the App component
class App extends Component {
constructor(props){
super(props)
this.state= {
users: [],
}
}
async componentDidMount() {
const url = ('https://jsonplaceholder.typicode.com/users')
const response = await fetch (url)
const data = await response.json()
this.setState({users: data.itemsList})
console.log({users: data.itemsList})
}
render() {
return (
<Users users = {this.state.users} />
)
}
}
export default App;
When you map over an array, whatever you return is returned each time for every element in the array. This code will create a whole new table for each user.
I think what you want to do is define your column headers separately from your map call and then map over the array to generate the rows:
const columns = ["ID", "Name", "Username", "Email"];
...
<div>
<div className="container smcontainer d-flex justify-content-start">
<div className="row">
<div className="col-md-12">
<table className="table table-striped">
<thead>
{columns.map(c => <th>{c}</th>)}
</thead>
<tbody>
{users.map((user, index) => (
<tr>
<td className=""> {user['id']} </td>
<td className=""> {user['name']} </td>
<td className=""> {user['username']} </td>
<td className=""> {user['email']} </td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</div>
I made a Codepen demonstrating both ways, but to me the first one makes much more sense.
That should give you what you want.
<thead>
{user['name']}
<thead/>
If you want to take the first name of Leanne Graham, you can do:
<thead>
{user['name'].split(" ")[0]}
<thead/>

Elements not rendering in react

I am doing a fetch and storing the result in my state "data", then i am sending this "data" to the function "Showingmovies" , there i am using table tag to align my layout . But the elements are not being rendered from the "Showingmovies" function, i have found that the props are being succesfully passed to the function still it is not rendering.
Below is my complete code -
import React, { Component } from 'react'
function Showingmovies(props){
console.log("in showingmovies",props.mov)
return(
<table>
<tbody>
{props.mov.results.map((mv) =>{
<tr>
<td>
<p>image here</p>
</td>
<td>
{mv.original_title}
<p>{mv.overview}</p>
</td>
</tr>
})}
</tbody>
</table>
)}
export default class movie extends Component {
constructor(){
super()
this.state={
data: [],
}
}
componentWillMount(){
fetch("https://api.themoviedb.org/3/search/movie?
api_key=ab85c167dc8f5538f5b6e08f01923243&query=J")
.then((res) =>res.json())
.then((data) => this.setState({
data: data,
}))
}
render() {
return (
<div>
<p>hi i will show you movies</p>
{this.state.data.length !== 0 ?
<Showingmovies mov = {this.state.data}/>:null}
</div>
)
}
}
You need to add return in the map method as
function Showingmovies(props){
return(
<table>
<tbody>
{props.mov.results.map((mv) =>{
return (
<tr>
<td>
<p>image here</p>
</td>
<td>
{mv.original_title}
<p>{mv.overview}</p>
</td>
</tr>
)
})}
</tbody>
</table>
)}
Problem is return
Solution
Whenever you use map you must use return if you use `{} like
{props.mov.results &&
props.mov.results.map(mv => {
return <tr>
<td>
<p>image here</p>
</td>
<td>
{mv.original_title}
<p>{mv.overview}</p>
</td>
</tr>;
})}
and if you are not don't want to return then remove {} like
{props.mov.results && props.mov.results.map(mv =>
<tr>
<td>
<p>image here</p>
</td>
<td>
{mv.original_title}
<p>{mv.overview}</p>
</td>
</tr>)
}
codeSandbox

Trying to use react to trigger different components with the same function

I am trying to make a an accordion using React but and I receive my data from a Json API which I dynamically use to make the table. All that works.
However when I try to make the toggleHidden function, it opens every single view instead of the one I just clicked on. How can I target a specific component when clicking instead of just using this
<Table striped bordered condensed hover>
<thead>
<tr>
<th>Asset #</th>
<th>Description</th>
<th>Person</th>
<th>Username</th>
<th>Room</th>
<th>ID</th>
<th>Patches</th>
<th>Date</th>
<th>Java</th>
<th>AACV</th>
</tr>
</thead>
{this.state.items.map((data, key) => {
return (
<tbody>
<tr
id={data.name}
onClick=`{this.toggleHidden.bind(this)}
>`
<td>{data.name}</td>
<td >{data.height}</td>
<td>{data.mass}</td>
<td>{data.gender}</td>
<td >{data.birth_year}</td>
<td>{data.eye_color}</td>
<td >{data.birth_year}</td>
<td>{data.eye_color}</td>
<td >{data.birth_year}</td>
<td>{data.eye_color}</td>
</tr>
<td
className={data.name}
colSpan="10"
>
{!this.state.isHidden && <Filled/>}
</td>
</tbody>
)
})}
</Table>
toggleHidden (e) {
this.setState({
isHidden: !this.state.isHidden
})
}
I Would suggest you to use the power of components.
Create component for row inside the table, something like this:
export default class Row extends React.Component {
render() {
const { data } = this.props;
<tbody>
<tr
id={data.name}
onClick=`{this.toggleHidden.bind(this)}
>`
<td>{data.name}</td>
<td >{data.height}</td>
<td>{data.mass}</td>
<td>{data.gender}</td>
<td >{data.birth_year}</td>
<td>{data.eye_color}</td>
<td >{data.birth_year}</td>
<td>{data.eye_color}</td>
<td >{data.birth_year}</td>
<td>{data.eye_color}</td>
</tr>
<td
className={data.name}
colSpan="10"
>
{!this.state.isHidden && <Filled/>}
</td>
</tbody>
}
toggleHidden() {
this.setState(state => ({
isHidden: !state.isHidden
}));
}
}
and in your main component do something like this:
<Table striped bordered condensed hover>
<thead>
<tr>
<th>Asset #</th>
<th>Description</th>
<th>Person</th>
<th>Username</th>
<th>Room</th>
<th>ID</th>
<th>Patches</th>
<th>Date</th>
<th>Java</th>
<th>AACV</th>
</tr>
</thead>
{this.state.items.map((data, key) => <Row data={data} />}
</Table>
it's more readable, it's more react style, and it's let each row to handle their own state
(there might be some syntax error, but the concept is working)
Good Luck!
Well you need to make nested state for each key that means
const { Table, Icon } = semanticUIReact;
const ITEMS = [
{
asset: "Asset 1",
description: "Description"
},
{
asset: "Asset 2",
description: "Description2"
}
];
class Example extends React.Component {
constructor(props) {
super(props);
this.state = { items: ITEMS };
this.renderRow = this.renderRow.bind(this);
}
render() {
return (
<Table striped bordered condensed selectable>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Asset</Table.HeaderCell>
<Table.HeaderCell>Description</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>{this.state.items.map(this.renderRow)}</Table.Body>
</Table>
);
}
renderRow(data, key) {
return (
<Table.Row onClick={() => this.toggleHidden(key)}>
<Table.Cell>{data.asset}</Table.Cell>
<Table.Cell textAlign="right">
{this.state[key] ? "This is hidden" : data.description}
</Table.Cell>
</Table.Row>
);
}
toggleHidden(key) {
this.setState({
[key]: !this.state[key]
});
}
}
ReactDOM.render(<Example />, document.getElementById("react"));
<link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.1/semantic.min.css" rel="stylesheet" />
<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>
<script src="https://cdn.jsdelivr.net/npm/semantic-ui-react#0.79.1/dist/umd/semantic-ui-react.min.js"></script>
<div id="react"></div>

Resources