I am working on a chating app with reactjs and i have my components as:
-ChatBox
|-MessageList
|-InputArea
I want to update(re-render,basically append a "li" and scroll down automatically) MessageList component if user provides an input from Input Area.
What i have thought so far is to pass ref from chatbox to MessageList and and then use those refs in InputArea. But i think it is not a good approach. Can someone suggest me what to do.
Update:
My MessageList code:
import React, { useContext,useRef, useState, useEffect } from 'react';
import Message from './Message';
import "./MessageList.css";
import { AuthContext } from '../../Shared/Context/AuthContext';
const MESSAGES=[{convId:1, msgId:1 ,from:'u1',to:'u2',msg:"hello"},
{convId:1, msgId:2 ,from:'u2',to:'u1',msg:"hi"},
{convId:1, msgId:3 ,from:'u1',to:'u2',msg:"how ru"},
{convId:1, msgId:4 ,from:'u2',to:'u1',msg:"goood!!!"},
{convId:1, msgId:5 ,from:'u1',to:'u2',msg:"party ?!"},
{convId:1, msgId:6 ,from:'u2',to:'u1',msg:"yeah!!!"}];
const MessageList=(props)=>{
const msgBoxRef=useRef();
const [messages,setMessages]=useState(MESSAGES);
useEffect(()=>{
msgBoxRef.current.scrollTop=msgBoxRef.current.scrollHeight;
},[messages]);
useEffect(()=>{
if(msgBoxRef.current.scrollTop===msgBoxRef.current.scrollHeight)
msgBoxRef.current.scrollTop=msgBoxRef.current.scrollHeight;
},[])
const auth =useContext(AuthContext);
return (<div className="messages" ref={msgBoxRef}>
<ul>
{messages.map((msg)=>(
<Message key={msg.msgId} className={(msg.from===auth.user.userName)?"sent":"replies"} imageSrc="http://emilcarlsson.se/assets/mikeross.png">{msg.msg}</Message>
))}
</ul>
</div>);
};
export default MessageList;
Input Component code:
import React from 'react';
import {InputGroup,FormControl,Button} from 'react-bootstrap';
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faPaperPlane } from "#fortawesome/free-solid-svg-icons";
const Input=(props)=>{
return (<InputGroup className="mb-0">
<FormControl className="input-msg shadow-none" placeholder="Enter new message..." aria-label="new task" />
<InputGroup.Append>
<Button className="send-btn shadow-none">
<FontAwesomeIcon icon={faPaperPlane} />
</Button>
</InputGroup.Append>
</InputGroup>);
};
export default Input;
ChatBox code:
//TODO: IMPLEMENT CHAT CARD
import React, { useState, useRef } from "react";
import { Card } from "react-bootstrap";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faArrowLeft } from "#fortawesome/free-solid-svg-icons";
import MessageList from "./MessageList";
import Input from "./Input";
import Conversations from "./Conversations";
import "./ChatBox.css";
const ChatBox = (props) => {
const [conversation, setConversation] = useState(null);
const [backBtn,setBackBtn]=useState(false);
const selectedConv = (profile) => {
setConversation(profile);
setBackBtn(true);
};
const backBtnHandler=()=>{
setConversation(null);
setBackBtn(false);
}
return (
<Card className="frame">
{conversation ? (
<React.Fragment>
<Card.Header><FontAwesomeIcon icon={faArrowLeft} onClick={backBtnHandler}/>{" "+conversation.name}</Card.Header>
<MessageList chatId={conversation.id}/>
<Input />
</React.Fragment>
) : (
<React.Fragment>
<Card.Header>Chatbox</Card.Header>
<Conversations selectedChat={selectedConv} />
</React.Fragment>
)}
</Card>
);
};
export default ChatBox;
Related
I added an OnClick event in a custom component created by me, but it didn't work. I want to add an OnClick event to this component. When i click it, it will play a youtube video below. Is it not possible to do this?
Here is my code:
import React, { useState } from "react";
import classes from "./Feature.module.css";
import { ButtonHorizontal, ButtonCircular } from "./Buttons";
function Feature() {
const [open, setOpen] = React.useState(false);
const openVideo = () => {
setOpen((prev) => !prev);
};
return (
<div>
<ButtonHorizontal icon={faPlay} text="Watch Video" onClick={openVideo}/>
{open ? (
<iframe
src="https://www.youtube.com/watch?v=k2qgadSvNyU"
frameborder="0"
allow="autoplay; encrypted-media"
allowfullscreen
title="video"
/>
) : null}
</div>
);
}
export default Feature;
And buttons.js:
import React from "react";
import classes from "./Buttons.module.css";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faPlay} from "#fortawesome/free-solid-svg-icons";
export function ButtonHorizontal(props) {
return (
<div className={classes.buttonHorizontal}>
<FontAwesomeIcon icon={props.icon} className={classes.icon}/>
<div className={classes.text}>{props.text}</div>
</div>
);
}
You need to attach the onClick property to an actual HTML element. Something like <div className={classes.buttonHorizontal} onclick={props.onClick}> should work.
import React from "react";
import classes from "./Buttons.module.css";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faPlay} from "#fortawesome/free-solid-svg-icons";
export function ButtonHorizontal(props) {
return (
<div className={classes.buttonHorizontal} onclick={props.onClick}>
<FontAwesomeIcon icon={props.icon} className={classes.icon}/>
<div className={classes.text}>{props.text}</div>
</div>
);
}
I have my homepage where I received the products with the redux method but I did not want to render them on the home page so I did it with a single product component, but again I wanted to display the products in react-Alice-carousel I sent the products in the homepage through props and destrusctured it in the Single product and tried to create the items props of react-alice-carousel through jsx but got an error product.map is not a function.
My Single Product Component.
import React from "react";
import AliceCarousel from "react-alice-carousel";
// import { Button, Card, Container } from "react-bootstrap";
// import { FaShoppingCart } from "react-icons/fa";
// import { useDispatch, useSelector } from "react-redux";
import { Link } from "react-router-dom";
// import { AddToCartAction, RemoveFromCart } from "../../actions/cartActions";
import UICARD from "../../interface/UICARD";
// import classes from "./home.module.css";
export function numberWithComas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
const SingleProduct = ({ product }) => {
const items = product.map((p) => {
return (
<Link to={`/product/${p._id}`}>
<UICARD>
<img src={p.image} alt={p.name} />
<span>
{p.name}
<span>{p.description}</span>
</span>
<span>{p.price}</span>
</UICARD>
</Link>
);
});
const responsive = {
0: {
items: 4,
},
512: {
items: 6,
},
};
return (
<div>
<AliceCarousel
items={items}
disableDotsControls
infinite
mouseTracking
responsive={responsive}
/>
</div>
);
};
export default SingleProduct;
My Home Page
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Products } from "../../actions/productsActions";
import ErrorMessage from "../../helpers/ErrorMessage";
import Loading from "../../helpers/Loading";
import SideBar from "../Home/SideBar";
import SingleProduct from "../Home/SingleProduct";
import classes from '../Home/home.module.css'
const Home = () => {
const dispatch = useDispatch();
const productList = useSelector((state) => state.productList);
const { products, error, loading } = productList
console.log(products);
useEffect(() => {
dispatch(Products());
},[dispatch])
return (
<div className ={classes.home}>
{error && <ErrorMessage variant={error.info?"info":"danger"}>{error}</ErrorMessage>}
{loading && <Loading />}
<SideBar />
<div className={classes.productContainer}>
{
products.map((product) => {
return <SingleProduct product={product} key={product._id} />
})
}
</div>
</div>
);
};
export default Home;
You need to change you home compoent like that since singleProduct need to have a props peroduct as an array , i recommand to use prop-types to avoid this kind of problems
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Products } from "../../actions/productsActions";
import ErrorMessage from "../../helpers/ErrorMessage";
import Loading from "../../helpers/Loading";
import SideBar from "../Home/SideBar";
import SingleProduct from "../Home/SingleProduct";
import classes from '../Home/home.module.css'
const Home = () => {
const dispatch = useDispatch();
const productList = useSelector((state) => state.productList);
const { products, error, loading } = productList
console.log(products);
useEffect(() => {
dispatch(Products());
},[dispatch])
return (
<div className ={classes.home}>
{error && <ErrorMessage variant={error.info?"info":"danger"}>{error}</ErrorMessage>}
{loading && <Loading />}
<SideBar />
<div className={classes.productContainer}>
<SingleProduct product={products} />
</div>
</div>
);
};
export default Home;
I'm programming with some friends a Chess App and now we get an Error implementing the Chess itself.
we get the error in the first const of the function as well as at the Export of App.jsx
Our GitHub Repo: Chedu
App.jsx
import React, { useEffect, useState } from "react";
import "./App.css";
import { gameSubject, initGame, resetGame } from "./Game";
import Board from "./Board";
function App() {
const [board, setBoard] = useState([]); //get Error here
const [isGameOver, setIsGameOver] = useState();
const [result, setResult] = useState();
const [turn, setTurn] = useState();
useEffect(() => {
initGame();
const subscribe = gameSubject.subscribe((game) => {
setBoard(game.board);
setIsGameOver(game.isGameOver);
setResult(game.result);
setTurn(game.turn);
});
return () => subscribe.unsubscribe();
}, []);
return (
<div className="container">
{isGameOver && (
<h2 className="vertical-text">
GAME OVER
<button onClick={resetGame}>
<span className="vertical-text"> NEW GAME</span>
</button>
</h2>
)}
<div className="board-container">
<Board board={board} turn={turn} />
</div>
{result && <p className="vertical-text">{result}</p>}
</div>
);
}
export default App(); //Error Anonymous Function
in Index.js we are Rendering the function and export it.
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
export default ReactDOM.render(
<React.StrictMode>
<DndProvider backend={HTML5Backend}>
<App />
</DndProvider>
</React.StrictMode>,
document.getElementById("root")
);
serviceWorker.unregister();
And at last we want to render the index.js in our ChessBoardPage
import React, { useState } from "react";
import {
StyleSheet,
Text,
View,
Image,
TouchableOpacity,
Dimensions,
Switch,
} from "react-native"; //components
import ReactDOM from "react-dom";
import cheduLogo from "../Pictures/Logo.png";
import loginPictureBlack from "../Pictures/login.png";
import loginPictureWhite from "../Pictures/login_white.png";
import registerPictureBlack from "../Pictures/register.png";
import registerPictureWhite from "../Pictures/register_white.png";
import userPictureBlack from "../Pictures/user.png";
import userPictureWhite from "../Pictures/user_white.png";
import ChessGame from "./ChessBoard/index";
const windowWidth = Dimensions.get("window").width;
const windowHeight = Dimensions.get("window").height;
const { width } = Dimensions.get("window");
const x = 100;
const y = 200;
export default class TempPage extends React.Component {
state = {
switchValue: false,
backgroundColor: "white",
SwitchLogin: loginPictureBlack,
SwitchRegister: registerPictureBlack,
SwitchUser: userPictureBlack,
SunMoon: "☀️",
ShadowBackgroundColor: "white",
};
handleSwitchBackground = () => {
[...]
}
};
render() {
let { backgroundColor } = this.state;
return (
<View
style={{
windowWidth,
windowHeight,
backgroundColor: this.state.backgroundColor,
}}
>
[...]
{/*Content*/}
<View stlye={{ flex: 1 }}>
<ChessGame />
</View>
</View>
);
}
}
[...]
sometime we have issues in react when using anonymous functions. Since anonymous functions aren’t assigned an identifier (via const/let/var), they aren’t persistent whenever this functional component inevitably gets rendered again. This causes JavaScript to allocate new memory each time this component is re-rendered instead of allocating a single piece of memory only once when using “named functions”
consider refactoring your code as follows
import React, { useEffect, useState } from "react";
import "./App.css";
import { gameSubject, initGame, resetGame } from "./Game";
import Board from "./Board";
const App = () => {
const [board, setBoard] = useState([]); //get Error here
const [isGameOver, setIsGameOver] = useState();
const [result, setResult] = useState();
const [turn, setTurn] = useState();
useEffect(() => {
initGame();
const subscribe = gameSubject.subscribe((game) => {
setBoard(game.board);
setIsGameOver(game.isGameOver);
setResult(game.result);
setTurn(game.turn);
});
return () => subscribe.unsubscribe();
}, []);
return (
<div className="container">
{isGameOver && (
<h2 className="vertical-text">
GAME OVER
<button onClick={resetGame}>
<span className="vertical-text"> NEW GAME</span>
</button>
</h2>
)}
<div className="board-container">
<Board board={board} turn={turn} />
</div>
{result && <p className="vertical-text">{result}</p>}
</div>
);
};
export default App;
I am not sure why you are using HTML tags in react native, which think are not yet supported in App.jsx. You should return a <View/> tag instead of div.
can't get the logout button to work properly. I'm using React and Firebase.
Here is a portion of the code from App.js, where the function was declared
imports
import React, { useState, useEffect } from "react";
import { fire } from './fire';
import LogIn from './LogIn';
import Hero from './Hero';
import './App.css';
declaration
const handleLogout = () => {
fire.auth().signOut();
};
And here is the code from the Hero.js, where the function is used
import React from 'react';
import Contact from "./components/Contact";
const Hero = (handleLogout) => {
return(
<section className="hero">
<nav>
<h2>Welcome</h2>
<button onClick = {handleLogout}>Log Out</button>
</nav>
<div id="contact-form">
<Contact />
</div>
</section>
)
}
export default Hero;
What I'm doing wrong?
you need to get the handleLogout from props properly:
const Hero = ({handleLogout}) => {...}
Why is useContext undefined?
Context
import React from 'react'
const PathContext = React.createContext()
export default PathContext
from a jsx file
import PathContext from '../../../contexts/pathContext';
.......
<PathContext.Provider
value={{
paths,
pathChecks
}}
>
<MyComponent />
</PathContext.Provider>
In MyComponent.jsx render function..
import PathContext from 'path/to/file';
import {useContext} from 'react';
const {
paths,
pathChecks
} = useContext(PathContext);
UNDEFINED!
What is my context undefined?
Use it like so:
App.js (or Main Component)
// App.js
import React from "react";
import MyContext from "./PathContext";
import MyComponent from "./MyComponent";
export default function App() {
return (
<div className="App">
<MyContext>
<MyComponent />
</MyContext>
</div>
);
}
PathContext.js
// PathContext.js
import React, { createContext } from "react";
export const PathContext = createContext(); // regular export
// A new component that will hold the context values and will wrap your <MyComponent>
const MyContext = ({ children }) => {
const paths = "define your paths";
const pathChecks = "define your pathChecks";
return (
<PathContext.Provider
value={{
paths,
pathChecks
}}
>
{children} // Pass children props
</PathContext.Provider>
);
};
export default MyContext; // default export MyContext component
MyComponent.js (context consumer)
// MyComponent.js
import React, { useContext } from "react";
import { PathContext } from "./PathContext";
const MyComponent = () => {
const { paths, pathChecks } = useContext(PathContext);
return (
<div>
<div>{paths} value</div>
<div>{pathChecks} value</div>
</div>
);
};
export default MyComponent;
Here is a sandbox example.
I think you need to use {useContext} from 'react' instead of {useContext} from React
Please check this example. It is working fine.
import React, {useContext, useEffect, useReducer, useState} from 'react';
import PathContext from "./PathContext";
function PathContextExample() {
const paths = 'this is path';
const pathChecks = 'this is path checks';
return (
<div>
<PathContext.Provider
value={{
paths,
pathChecks
}}
>
<MyComponent/>
</PathContext.Provider>
</div>
)
}
export default PathContextExample;
function MyComponent() {
const {
paths,
pathChecks
} = useContext(PathContext);
return (
<div>
{paths}
<br/>
{pathChecks}
</div>
)
}
In MyComponent.jsx file you should import PathContext