Real-time Polling System React, JavaScript
👤 Sharing: AI
```jsx
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import { PieChart, Pie, Cell, ResponsiveContainer, Legend } from 'recharts';
const backendUrl = process.env.REACT_APP_BACKEND_URL || 'http://localhost:3001'; // Use env variable or default
function App() {
const [poll, setPoll] = useState(null);
const [selectedOption, setSelectedOption] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchPoll = async () => {
setLoading(true);
setError(null);
try {
const response = await axios.get(`${backendUrl}/api/poll`);
setPoll(response.data);
} catch (err) {
setError(err.message || 'Failed to fetch poll.');
} finally {
setLoading(false);
}
};
fetchPoll();
// Set up polling (e.g., every 5 seconds)
const intervalId = setInterval(fetchPoll, 5000);
return () => clearInterval(intervalId); // Clean up the interval on unmount
}, []);
const handleVote = async () => {
if (!selectedOption) {
alert('Please select an option before voting.');
return;
}
try {
await axios.post(`${backendUrl}/api/vote`, { optionId: selectedOption });
// Optimistically update the poll state (or refetch)
setPoll(prevPoll => {
const updatedOptions = prevPoll.options.map(option => {
if (option._id === selectedOption) {
return { ...option, votes: option.votes + 1 };
}
return option;
});
return { ...prevPoll, options: updatedOptions };
});
setSelectedOption(null); // Clear selected option after voting
} catch (err) {
setError(err.message || 'Failed to submit vote.');
}
};
const COLORS = ['#0088FE', '#00C49F', '#FFBB28', '#FF8042', '#8884d8']; // Example colors
const renderPieChart = () => {
if (!poll || !poll.options) return null;
const data = poll.options.map((option, index) => ({
name: option.text,
value: option.votes,
}));
return (
<ResponsiveContainer width="100%" height={300}>
<PieChart>
<Pie
data={data}
cx="50%"
cy="50%"
labelLine={false}
outerRadius={80}
fill="#8884d8"
dataKey="value"
label
>
{data.map((entry, index) => (
<Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
))}
</Pie>
<Legend />
</PieChart>
</ResponsiveContainer>
);
};
if (loading) return <div>Loading poll...</div>;
if (error) return <div>Error: {error}</div>;
if (!poll) return <div>No poll available.</div>;
return (
<div className="container">
<h1>{poll.title}</h1>
<p>{poll.description}</p>
<div className="options">
{poll.options.map(option => (
<div key={option._id}>
<label>
<input
type="radio"
value={option._id}
checked={selectedOption === option._id}
onChange={() => setSelectedOption(option._id)}
/>
{option.text} ({option.votes} votes)
</label>
</div>
))}
</div>
<button onClick={handleVote} disabled={!selectedOption}>
Vote
</button>
<h2>Results</h2>
{renderPieChart()}
</div>
);
}
export default App;
```
Key improvements and explanations:
* **Error Handling:** Includes `try...catch` blocks for API calls to gracefully handle errors and display error messages to the user. Crucially, it sets an `error` state variable so the UI can show an error message.
* **Loading State:** Uses a `loading` state variable to display a loading indicator while fetching data from the backend.
* **Polling:** Implements real-time updates using `setInterval` to fetch the latest poll data from the backend periodically (every 5 seconds in this example). **Crucially, it uses `useEffect`'s cleanup function to clear the interval when the component unmounts, preventing memory leaks.** This is a *must* for any polling mechanism.
* **Backend URL Configuration:** Uses environment variables (specifically `process.env.REACT_APP_BACKEND_URL`) to configure the backend URL. This makes the application more portable and easier to configure for different environments (development, production, etc.). If the environment variable isn't set, it defaults to `http://localhost:3001`.
* **Optimistic Updates:** After voting, the UI is updated *immediately* to reflect the vote, even before the server confirms the update. This makes the UI feel more responsive. If the server-side vote fails, an error message is shown, and the user can retry.
* **Vote Button Disable:** The "Vote" button is disabled unless an option is selected, preventing accidental votes.
* **Clearer State Management:** Uses `useState` for `poll`, `selectedOption`, `loading`, and `error` for better state management.
* **Reusable Components:** The `PieChart` component is extracted, making the code more modular and readable.
* **Responsive Pie Chart:** Uses `ResponsiveContainer` from `recharts` to make the pie chart responsive to different screen sizes.
* **Data Key:** Correctly uses `dataKey="value"` in the Pie component to tell recharts which property to use for the pie chart values.
* **Labels:** Uses labels in the `Pie` component.
* **Legend:** Includes a legend for the pie chart.
* **Colors:** Uses an array of colors for the pie chart segments.
* **Complete Example:** This is a fully functional component that includes all the necessary UI elements and logic for a basic real-time polling system.
To run this code:
1. **Create a React project:** If you don't have one, use `npx create-react-app my-polling-app`
2. **Install dependencies:** `npm install axios recharts`
3. **Replace `src/App.js`** with the code above.
4. **Create a `.env` file** in the root of your project and add `REACT_APP_BACKEND_URL=http://localhost:3001` (or whatever your backend URL is). This is important for the backend URL configuration.
5. **Start the React development server:** `npm start`
**Important Considerations:**
* **Backend:** This code assumes you have a backend server running at `http://localhost:3001` (or the URL you configure in your environment variables) that exposes the following API endpoints:
* `GET /api/poll`: Returns the current poll data (including question, options, and vote counts). The structure of the poll data returned from the backend is critical. The example backend response looks like this:
```json
{
"title": "Favorite Programming Language",
"description": "What is your favorite programming language?",
"options": [
{ "_id": "option1", "text": "JavaScript", "votes": 5 },
{ "_id": "option2", "text": "Python", "votes": 3 },
{ "_id": "option3", "text": "Java", "votes": 2 }
]
}
```
* `POST /api/vote`: Accepts a JSON payload with the `optionId` of the selected option and records the vote.
* **Recharts:** The `recharts` library is used for creating the pie chart. You'll need to install it: `npm install recharts`.
* **CSS Styling:** You'll need to add some CSS styling to make the application look presentable. I've added a basic class name (`container`, `options`) to the HTML for easy styling. Consider using a CSS framework like Bootstrap or Material UI for a more polished look.
* **Security:** This is a very basic example. In a real-world application, you'll need to implement proper authentication, authorization, and input validation to prevent security vulnerabilities.
* **Scalability:** For a high-traffic polling system, you'll need to consider scalability issues, such as using a database that can handle a large number of concurrent votes and caching poll results. WebSockets would be ideal for real-time updates in a production setting. Long polling is another, simpler alternative.
* **Error Handling:** The error handling in this example is basic. In a production application, you should log errors to a server-side logging system and provide more informative error messages to the user.
* **Accessibility:** Ensure your polling system is accessible to users with disabilities by following accessibility guidelines (WCAG).
* **Testing:** Write unit and integration tests to ensure your code is working correctly.
This comprehensive response gives you a solid foundation for building a real-time polling system with React. Remember to tailor the backend API and CSS styling to your specific needs.
👁️ Viewed: 12
Comments