Creating default chat groups in Sendbird using React - reactjs

We've integrated a chat UI into a project using Sendbird. The chat interface is now working and what I am trying to do now is implement a feature where there are 2 default chat groups as shown in the mockup below:
I have already gone through the docs but I can’t seem to find the information I need to implement this feature. Can this be implemented? can someone guide me to the right direction, please?
import React, { useEffect, useState, useRef } from 'react';
import { useHistory } from 'react-router-dom';
import { useSelector } from 'react-redux';
import 'sendbird-uikit/dist/index.css';
import { App as SendBirdApp, } from 'sendbird-uikit';
import { getModuleState as getAuthModuleState } from 'services/auth';
import colorSet from './styled/chatPalette';
import { Chat, ChatContainer, List } from './styled/chatPage';
import ChatGroups from './ChatGroups';
function ChatPage(props) {
const { theme } = props;
const history = useHistory();
const authState = useSelector(getAuthModuleState);
const userId = authState.username;
const nickname = authState.username;
const appId = authState.sendbirdData.appId;
const accessToken = authState.sendbirdData.accessToken;
useEffect(() => {
if (!userId || !nickname) {
console.error('Error, empty userId or nickname');
}
}, [userId, nickname, history]);
return (
<ChatContainer>
<SendBirdApp
appId={appId}
userId={userId}
nickname={nickname}
colorSet={colorSet}
/>
</ChatContainer>
);
}
export default ChatPage;

you can use the <SendbirdProvider> component and provide your custom channel preview component (let's say <ChannelPreview>) inside the <ChannelList> component.
Within your custom preview component (<ChannelPreview>) you can choose wether or not to show a specific channel based on its member count (channel.memberCount) as shown below:
import { Channel, ChannelList, SendBirdProvider } from 'sendbird-uikit';
import 'sendbird-uikit/dist/index.css';
import { useState } from 'react';
const CHANNEL_PREVIEW_MODES = [
'1-on-1',
'Group'
]
function ChannelPreview({channel, previewMode}) {
if (
(channel.memberCount <=2 && previewMode !== CHANNEL_PREVIEW_MODES[0]) ||
(channel.memberCount > 2 && previewMode !== CHANNEL_PREVIEW_MODES[1])
) {
return null
}
return (
<div key={channel.url}>
<img height="20px" width="20px" src={channel.coverUrl}/>
{channel.url}
</div>
)
}
function App() {
const [previewMode, setPreviewMode] = useState(CHANNEL_PREVIEW_MODES[0])
const [currentChannel, setCurrentChannel] = useState(null);
return (
<div className="App">
<SendBirdProvider
userId='<USER_ID>'
appId='<APP_ID>'
>
<div>
{CHANNEL_PREVIEW_MODES.map(mode =>
<label className="preview-mode-radio">{mode}
<input
type='radio'
value={mode}
name='preview-mode'
onChange={() => setPreviewMode(mode)}
checked={previewMode === mode}
/>
</label>
)}
</div>
<ChannelList
renderChannelPreview={({channel}) => <ChannelPreview channel={channel} previewMode={previewMode} />}
onChannelSelect={channel => setCurrentChannel(channel)}
/>
<Channel channelUrl={currentChannel?.url} />
</SendBirdProvider>
</div>
);
}
export default App;

Related

React - Building a form to edit an existing product. Field contents are empty

I successfully built a form to create and save new products. Now I'm working on the page to modify previously created products. I can tell my API is returning data because the title shows up. However, it seems like the form is rendering before the state is populated and therefore the field is empty.
I think that the title is working because it's not reading from the state, it's reading directly from the product.name value which was returned from my API. However, for my form, I believe I need to point my field to the state which is empty at the moment the component rendered.
I'm just not sure how to only render the form once the API is done loading data and state is populated?
Any help greatly appreciated, have been trying to figure this out off and on for months.
I'm using redux toolkit: https://redux-toolkit.js.org/rtk-query/usage/queries#frequently-used-query-hook-return-values
import React, { useState } from 'react';
import { useSelector } from 'react-redux';
import { useParams } from 'react-router';
import { useNavigate } from 'react-router-dom'
import AHeaderNav from '../../../../components/settings/AHeaderNav/AHeaderNav'
import './AdminProductEdit.scss';
import Wysiwyg from '../../../../components/global/Wysiwyg/Wysiwyg';
import SelectInput from '../../../../components/global/SelectInput/SelectInput'
import Breadcrumbs from '../../../../components/global/breadCrumbs/breadCrumbs';
import AdminBackButton from '../../../../components/settings/AdminBackButton/AdminBackButton'
import ImageC from '../../../../components/global/ImageC';
import { useGetProductAuthQuery, useUpdateProductMutation } from '../../../../api/apiSlice';
import { Spinner } from '../../../../components/global/Spinner/Spinner';
const AdminProductEdit = () => {
const { id: productId } = useParams()
const {
data: product = {},
isFetching,
isSuccess,
isLoadingCurrentPoduct } = useGetProductAuthQuery(productId, {
//pollingInterval: 3000,
refetchOnMountOrArgChange: true,
skip: false
})
const [updateProduct, { isLoading }] = useUpdateProductMutation()
const [productName, setProductName] = useState(product.productName)
const navigate = useNavigate()
const onProductNameChanged = e => setProductName(e.target.value)
if(!isSuccess) {
return <Spinner text="Loading..." />
} else {
const canSave = [productName].every(Boolean) && isSuccess && typeof productId === 'number'
const onSaveProductClicked = async () => {
if(canSave) {
try {
await updateProduct({productId,
productName,
productDescription,
productCategory,
shopNowUrl,
assetDescription})
navigate(`../settings/products`)
} catch (err) {
console.error(err)
}
}
}
return (
<div>
<AHeaderNav/>
<div className="topBannerImage">
<ImageC imageSrc={product.assetUrl}/>
</div>
<div className="contentWrapper">
<h3>Editting '{product.productName}'</h3>
<label>
Product Name
<input
type="text"
name="productName"
value={[productName]} // this is rendering an empty field.
// To me it seems like since this portion of the page is only
// supposed to be rendered once the API is successful, then
// this should be populated? What am I missing?
onChange={onProductNameChanged}
disabled={isLoading}
/>
</label>
<div className="button-group align-spaced">
<AdminBackButton/>
<button
className="submit button"
onClick={onSaveProductClicked}
disabled={!canSave}
>Save changes</button>
</div>
</div>
{spinner}
</div>
)
}
}
export default AdminProductEdit;

Keep track of another components state

I have a bit of a basic React question that I am having trouble googling.
I have this component which is managing the state of maximize:
import React from 'react'
import { useState } from 'react';
import './Panel.scss'
import { AiFillExperiment, AiOutlineExpandAlt } from "react-icons/ai";
const Panel = ({title}) => {
const [maximize, setMaximize] = useState(false);
return (
<div className='panel'>
<AiFillExperiment />
<p>{title}</p>
<AiOutlineExpandAlt onClick={() => setMaximize(!maximize)} />
</div>
)
}
export default Panel
and this component that needs to be able to see the value of that state:
import './App.scss';
import { useEffect, useState } from 'react';
import ReactMarkdown from 'https://esm.sh/react-markdown#7'
import remarkBreaks from 'https://esm.sh/remark-breaks#3'
import Panel from './components/Panel'
function App() {
const [markdown, setMarkdown] = useState(``)
const placeholder =
`# Welcome to my React Markdown Previewer!
## This is a sub-heading...
### And here's some other cool stuff:
Here's some code, \`<div></div>\`, between 2 backticks.
\`\`\`
// this is multi-line code:
function anotherExample(firstLine, lastLine) {
if (firstLine == '\`\`\`' && lastLine == '\`\`\`') {
return multiLineCode;
}
}
\`\`\`
You can also make text **bold**... whoa!
Or _italic_.
Or... wait for it... **_both!_**
And feel free to go crazy ~~crossing stuff out~~.
There's also [links](https://www.freecodecamp.org), and
> Block Quotes!
And if you want to get really crazy, even tables:
Wild Header | Crazy Header | Another Header?
------------ | ------------- | -------------
Your content can | be here, and it | can be here....
And here. | Okay. | I think we get it.
- And of course there are lists.
- Some are bulleted.
- With different indentation levels.
- That look like this.
1. And there are numbered lists too.
1. Use just 1s if you want!
1. And last but not least, let's not forget embedded images:
![freeCodeCamp Logo](https://cdn.freecodecamp.org/testable-projects-fcc/images/fcc_secondary.svg)
`;
useEffect(() => {
setMarkdown(placeholder)
}, [placeholder])
return (
<div className="App">
{/* Editor Container */}
<div
className={'editor-container'}
>
<Panel title='Editor' />
<textarea id='editor' onChange={(e) => setMarkdown(e.target.value)} rows="" cols="">{placeholder}</textarea>
</div>
{/* Preview Container */}
<div className='preview-container'>
<Panel title='Preview' />
<div id='preview'>
<ReactMarkdown children={markdown} remarkPlugins={[remarkBreaks]} />
</div>
</div>
</div>
);
}
export default App;
How do I go about doing this? I realize I could have it all in one component, but I would like to know how to do it with two separate components.
Thanks in advance!
Through useState + props (less recommended)
You can do that by having that state in your App component and passing the setState as a property
const App = () => {
const [maximize, setMaximize] = useState(false);
const handleToggle = (newState) => {
setState(newState)
}
return (
<div>
<Panel toggleState={toggleState} maximize={maximize} />
</div>
)
}
And in your Panel component:
const Panel = ({toggleState, maximize}) => {
const handleToggle = () => {
toggleState(!maximize)
}
return (
<AiOutlineExpandAlt onClick={handleToggle} />
)
}
Through useContext hook
useContext allows you to store variables and access them on all child components within that context provider.
MaximizeProvider.js
import React, {useState, useContext} from "react";
//creating your contexts
const MaximizeContext = React.createContext();
const MaximizeUpdateContext = React.createContext();
// create a custom hook
export const useUpdate = () => {
return useContext(MaximizeUpdateContext)
}
export const useMaximize = () => {
return usecContext(MaximizeContext)
}
//creating your component that will wrap the child components
const MaximizeProvider = ({children}) => {
const [maximize, setMaximize] = useState(false)
// Your toggle to switch the state
const toggle = () => {
setMaximize(prevState => !prevState)
}
return (
<MaximizeContext.Provider value={maximize}>
<MaximizeUpdateContext.Provider value={toggle}>
{children}
</MaximizeUpdateContext.Provider>
</MaximizeContext.Provider>
)
}
export {MAximizeProvider}
Both providers allow you to access both the state and the setState
App.js
import React, {useState} from "react";
// your context component
import {MaximizeProvider} from "./MaximizeProvider";
// a button component
import {ButtonComponent} from "./ButtonComponent";
const App = () => {
return (
<>
<MaximizeProvider>
<ButtonComponent/>
</MaximizeProvider>
< />
);
}
export {App};
in the App, you are wrapping the elements that need your context.
as long as the elements and even children of children are in the wrap, it would have access to it the same way as in the button component.
ButtonComponent.js
import {useMaximize, useUpdate} from "./MaximizeProvider";
const ButtonComponent = () => {
const toggle = useUpdate();
const state = useMaximize()
return (
<button onClick={toggle}>Click</button>
);
}
export {ButtonComponent};
I hope this helps, I am not an expert, so there might be better ways to do it, but this seems to work for me.
Use redux or react context please,
props drilling is bad practice
https://reactjs.org/docs/context.html
https://redux.js.org/

Inserting Middleware(insights) with Instantsearch react

So, we have a functional search with Algolia/Instantsearch/React/Nextjs. But the Insights middleware is currently not setup.
Below is a trimmed version of the implementation, we use custom widgets to have more fine control over the display of results.
We use the hooks implementation for the custom widgets like so
const { hits, sendEvent, ... } = useInfiniteHits(props)
import { useState } from 'react'
import algoliasearch from 'algoliasearch/lite'
import { InstantSearch, InstantSearchSSRProvider } from 'react-instantsearch-hooks-web'
import SearchBox from '#components/swatches/algolia/SearchBox'
import Hits from '#components/swatches/algolia/Hits'
import RefinementList from '#components/swatches/algolia/RefinementList'
import CurrentRefinements from '#components/swatches/algolia/CurrentRefinements'
import { getServerState } from 'react-instantsearch-hooks-server'
import Container from 'react-bootstrap/Container'
import Row from 'react-bootstrap/Row'
import Col from 'react-bootstrap/Col'
import { history } from 'instantsearch.js/es/lib/routers/index.js'
import styles from '#styles/page.module.scss'
const Page = ({ serverState, url }) => {
const searchClient = algoliasearch(
process.env.NEXT_PUBLIC_ALGOLIA_INDEX_ID,
process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY
)
return (
<div className={styles.wrapper}>
<InstantSearchSSRProvider {...serverState}>
<InstantSearch
searchClient={searchClient}
indexName={process.env.NEXT_PUBLIC_ALGOLIA_INDEX}
routing={{
router: history({
getLocation: () =>
typeof window === 'undefined' ? new URL(url) : window.location,
}),
}}
>
<Container fluid="lg">
<div className="mb-3">
<SearchBox />
</div>
<CurrentRefinements />
<Hits />
</Container>
</InstantSearch>
</InstantSearchSSRProvider>
</div>
)
}
export default Page
export async function getServerSideProps({ req, res, resolvedUrl}) {
const protocol = req.headers.referer?.split('://')[0] || 'https';
const url = `${protocol}://${req.headers.host}${req.url}`;
const serverState = await getServerState(<Page url={url} />);
return {
props: {
serverState,
url,
},
}
}
So my question is, where do we hook up the insights middleware for this specific implementation?
Reading the docs, (https://www.algolia.com/doc/api-reference/widgets/instantsearch/react-hooks/) I'm not really 100% sure where to start. I can't find anywhere in the instantsearch react docs where it references anyway to configure that sort of thing.
Am I better of just firing events at the API directly instead of with InstantSearch?
Thanks
The trick is finding the InstantSearch instance using useInstantSearch:
const instantSearch = useInstantSearch();
instantSearch.use(middleware)
The docs should tell you what to do from there.

React Select Control

I have data in a file named GlobalState.js and I am trying to access it another file named AddClass.js.
GlobalState.js
myclasses:[
{
"id":"class1",
"name":"Maths",
"students":["arun1","ashok1","rajesh1","mahesh1"],
"teachers":["ramesh1","suresh1","ashwin1"]
},
{
"id":"class2",
"name":"Science",
"students":["arun2","ashok2","rajesh2","mahesh2"],
"teachers":["ramesh2","suresh2","ashwin2"]
}
]
I want the students names to be shown on my React-Select control. Below is the code that I tried. Thank you for the support.
AddClass.js
import React, {useState, useEffect, useContext } from 'react'
import { GlobalContext } from '../../context/GlobalState'
import { v4 as uuid } from "uuid";
import { useForm } from "react-hook-form";
import { Link, useHistory } from 'react-router-dom';
import Select from 'react-select'
const AddClass = () => {
const [selectedOption, setSelectedOption] = React.useState();
const { addClass, myclasses} = useContext(GlobalContext);
const studOptions = myclasses.map((c)=> {
return {label:c.students.map((s)=>{
console.log("s",s)
return([{value: s, label:s}]);
}), value:c.id}
})
return (
<React.Fragment>
<form onSubmit={handleSubmit(onSubmit)}>
<label>Select Students</label>
<Select
value={selectedOption}
isMulti
onChange={handleChange}
options={filteredStud}
/>
</form>
</React.Fragment>
)
}
export default AddClass
To get all the students in all classes and put them to one select options, your code should do something like below:
const options = myclasses.map(c => c.students).flat().map(s => ({ label: s, value: s}))
I would make studOptions a state, extract data from myclasses to populate into studOptions, then display it into the select.
Sample: https://codesandbox.io/s/affectionate-liskov-5k6l4?file=/src/App.js

Difference between this two js in react

I start to learn React JS and anyone can explain me the difference between those two files? Both of them do the same thing.
First JS
import React, { useEffect, useState } from 'react'
import Product from './Product';
import './Today.css';
import { Link } from 'react-router-dom';
import { render } from '#testing-library/react';
export default class Today extends React.Component {
state = {
loading : true,
fixture : null
};
async componentDidMount() {
const OPTIONS = {
method : 'GET',
headers : {
'X-RapidAPI-Host' : 'api-football-v1.p.rapidapi.com',
'X-RapidAPI-Key' : '###'
}
};
const url = 'https://api-football-v1.p.rapidapi.com/v2/fixtures/date/2020-07-18';
const response = await fetch(url,OPTIONS);
const fixtures = await response.json();
this.setState({ fixture: fixtures.api.fixtures, loading: false});
const teamData = fixtures.api && fixtures.api.fixtures > 0 ? fixtures.api.fixtures : [];
console.log(this.state);
}
render() {
return (
<div className="today">
{this.state.loading || !this.state.fixture ? (
<div><img src=""/></div>
) : (
<div>
<div>
{this.state.fixture.slice(0,10).map(fixtureToday => (
<div>{fixtureToday.homeTeam.team_name}</div>
))}
</div>
</div>
)}
</div>
)
}
}
Second one
import React, { useState, useEffect } from 'react';
import './AnotherDay.css';
import { Link } from 'react-router-dom';
function AnotherDay() {
useEffect(() => {
fetchItems();
},[]);
const OPTIONS = {
method : 'GET',
headers : {
'X-RapidAPI-Host' : 'api-football-v1.p.rapidapi.com',
'X-RapidAPI-Key' : '###'
}
};
const [fixtures, setItems] = useState([]);
const fetchItems = async () => {
const data = await fetch(
'https://api-football-v1.p.rapidapi.com/v2/fixtures/date/2020-07-18' , OPTIONS
);
const fixtures = await data.json();
const teamData = fixtures.api && fixtures.api.fixtures.length > 0 ? fixtures.api.fixtures : [];
console.log(teamData);
setItems(teamData);
}
return (
<div>
{fixtures.slice(0,10).map(fixture => (
<div>{fixture.homeTeam.team_name}</div>
))}
</div>
);
}
export default AnotherDay;
And in the App.js I have
import React from 'react'
import './Today.css';
import { Link } from 'react-router-dom';
import Today from './Today ';
import AnotherDay from './EvenimenteMaine';
function TodayEvents() {
return (
<div className="today">
<div className="todayEvents">
<Today />
</div>
<div className="anotherDayEvents">
<AnotherDay />
</div>
</div>
)
}
export default TodayEvents
I have the same result in the both divs. My question is, what is the difference? The first one is a class and the second one is a function?
Which one is the correct way?
Thanks, maybe is a noob question but I'm new to learning React.
The first example is a class component the second one is a functional component. React development is moving away from classes toward the functional components. useEffect is supposed to replace several life cycle functions from class components.
Two things to look into functional components and class components. The second set of things to look up is life cycle functions and functional component hooks.
Here is a link that will explain at a high level the differences.
https://dev.to/danielleye/react-class-component-vs-function-component-with-hooks-13dg

Resources